-
Notifications
You must be signed in to change notification settings - Fork 0
Go: Only output origin constraints for methods #11
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
base: qa/agent-github-codeql/pr-11-22399/base
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,8 +38,16 @@ type typeParamParentEntry struct { | |
| isFromReceiver bool | ||
| } | ||
|
|
||
| // typeParamMutex protects typeParamParent. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shipwright · LOW The comment above typeParamOriginMutex incorrectly says 'typeParamMutex protects typeParamOrigin.' It should reference typeParamOriginMutex. Impact: The comment above typeParamOriginMutex incorrectly says 'typeParamMutex protects typeParamOrigin.' It should reference typeParamOriginMutex. This is a small but real documentation defect that can mislead someone reasoning about lock ownership. Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shipwright · LOW The comment for typeParamOriginMutex says 'typeParamMutex protects typeParamOrigin.' It should say 'typeParamOriginMutex protects typeParamOrigin.' This is a documentation defect i Impact: The comment for typeParamOriginMutex says 'typeParamMutex protects typeParamOrigin.' It should say 'typeParamOriginMutex protects typeParamOrigin.' This is a documentation defect introduced by the diff. Suggested fix: Fix the review finding before release. |
||
| var typeParamParentMutex sync.RWMutex | ||
|
|
||
| var typeParamParent map[*types.TypeParam]typeParamParentEntry = make(map[*types.TypeParam]typeParamParentEntry) | ||
|
|
||
| // typeParamMutex protects typeParamOrigin. | ||
| var typeParamOriginMutex sync.RWMutex | ||
|
|
||
| var typeParamOrigin map[*types.TypeParam]*types.TypeParam = make(map[*types.TypeParam]*types.TypeParam) | ||
|
|
||
| func init() { | ||
| // this sets the number of threads that the Go runtime will spawn; this is separate | ||
| // from the number of goroutines that the program spawns, which are scheduled into | ||
|
|
@@ -1658,29 +1666,7 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { | |
| for i := 0; i < origintp.NumMethods(); i++ { | ||
| meth := origintp.Method(i).Origin() | ||
| extractMethod(tw, meth) | ||
|
|
||
| // Consider a generic struct and a generic method: | ||
| // | ||
| // type S[P any] struct{} | ||
| // func (*S[P]) m[Q any](x Q) {} | ||
| // | ||
| // If we have a variable 's' of type 'S[int]' and the expression | ||
| // 's.m[string]("")', then the type of the selector expression 's.m' | ||
| // is ' func(Q)'. The method 'm' here is an instantiation of the | ||
| // declaration, which has its own type with type parameter 'Q'. | ||
| // As we do not extract method instantiations, 'populateTypeParamParents' | ||
| // does not automatically get called for the type parameter 'Q' | ||
| // from the instantiation of 'm'. To compensate, we add the type | ||
| // parameters here. | ||
| // | ||
| // As a parent we use the origin method. This suffices, as the name | ||
| // and index of the type parameter in the instantiation will be | ||
| // identical to those of the uninstantiated method, and as only | ||
| // these two properties will be extracted for a type parameter. | ||
| if tp.Method(i) != meth { | ||
| signature := tp.Method(i).Type().(*types.Signature) | ||
| populateTypeParamParents(signature.TypeParams(), meth, false) | ||
| } | ||
| populateTypeParamParentsAndOrigins(tp.Method(i), meth) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shipwright · HIGH The new helper populateTypeParamParentsAndOrigins does two unrelated things: it records parents and records origins. Impact: The new helper populateTypeParamParentsAndOrigins does two unrelated things: it records parents and records origins. The name is a conjunction of two responsibilities, and the body immediately delegates to two separate functions. A future maintainer reading extractType will not know from the call site which side effects are essential for correctness and which are incidental. Splitting the call into populateTypeParam… Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright. |
||
| } | ||
|
|
||
| underlyingInterface, underlyingIsInterface := underlying.(*types.Interface) | ||
|
|
@@ -1704,7 +1690,8 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { | |
| case *types.TypeParam: | ||
| kind = dbscheme.TypeParamType.Index() | ||
| parentlbl, isReceiverChild := getTypeParamParentLabel(tw, tp) | ||
| constraintLabel := extractType(tw, tp.Constraint()) | ||
| constraint := getTypeParamOrigin(tp).Constraint() | ||
| constraintLabel := extractType(tw, constraint) | ||
| dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), constraintLabel, parentlbl, tp.Index(), isReceiverChild) | ||
| case *types.Union: | ||
| kind = dbscheme.TypeSetLiteral.Index() | ||
|
|
@@ -2062,7 +2049,10 @@ func getObjectBeingUsed(tw *trap.Writer, ident *ast.Ident) types.Object { | |
| } | ||
|
|
||
| func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) (trap.Label, bool) { | ||
| typeParamParentMutex.RLock() | ||
| entry, exists := typeParamParent[tp] | ||
| typeParamParentMutex.RUnlock() | ||
|
|
||
| if !exists { | ||
| log.Fatalf("Parent of type parameter does not exist: %s %s", tp.String(), tp.Constraint().String()) | ||
| } | ||
|
|
@@ -2074,6 +2064,9 @@ func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) (trap.Label, | |
| } | ||
|
|
||
| func setTypeParamParent(tp *types.TypeParam, parent types.Object, isFromReceiver bool) { | ||
| typeParamParentMutex.Lock() | ||
| defer typeParamParentMutex.Unlock() | ||
|
|
||
| entry, exists := typeParamParent[tp] | ||
| newEntry := typeParamParentEntry{parent, isFromReceiver} | ||
| if !exists { | ||
|
|
@@ -2121,3 +2114,76 @@ func checkObjectNotSpecialized(obj types.Object) { | |
| } | ||
| } | ||
| } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shipwright · HIGH getTypeParamOrigin falls back to returning the input type parameter when no origin is recorded. Impact: getTypeParamOrigin falls back to returning the input type parameter when no origin is recorded. This silently masks missing-origin bugs: if populateTypeParamOrigins is not called for some instantiated method, extraction proceeds with the instantiated constraint instead of the origin constraint, producing subtly wrong database rows rather than failing loudly. The existing getTypeParamParentLabel uses log.Fatalf for a… Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright. |
||
| // getTypeParamOrigin returns the origin type parameter for a type parameter | ||
| // from an instantiated method. | ||
| func getTypeParamOrigin(tp *types.TypeParam) *types.TypeParam { | ||
| typeParamOriginMutex.RLock() | ||
| origin, exists := typeParamOrigin[tp] | ||
| typeParamOriginMutex.RUnlock() | ||
|
|
||
| if exists { | ||
| return origin | ||
| } else { | ||
| return tp | ||
| } | ||
| } | ||
|
|
||
| // populateTypeParamParentsAndOrigins records for each type parameter of a method | ||
| // the origin parent and type parameter. | ||
| // | ||
| // Consider a generic struct and a generic method: | ||
| // | ||
| // type S[P any] struct{} | ||
| // func (*S[P]) m[Q ~P](x Q) {} | ||
| // | ||
| // If we have a variable 's' of type 'S[int]' and the expression 's.m[int](42)', | ||
| // then the type of the selector expression 's.m' is 'func[Q ~int](Q)'. The | ||
| // method 'm' here is an instantiation of the declaration, which has its own | ||
| // type with type parameter 'Q' with constraint 'interface { ~int }'. As we | ||
| // do not extract method instantiations, but only their origins, we want to | ||
| // match this behavior for the constraints of instantiated type parameter, and | ||
| // record their origin. Moreover, not extracting instantiations also means that | ||
| // 'populateTypeParamParents' does not automatically get called on their type | ||
| // parameters. To compensate, we add the type params here by calling | ||
| // `setTypeParamParent`. | ||
| // | ||
| // As the parent of a type parameter use the origin method. This suffices, as | ||
| // the name and index of the type parameter in the instantiation will be | ||
| // identical to those of the uninstantiated method, and as only a constraint and | ||
| // these two properties will be extracted for a type parameter. | ||
| func populateTypeParamParentsAndOrigins(meth *types.Func, originmeth *types.Func) { | ||
| if meth == originmeth { | ||
| return | ||
| } | ||
|
|
||
| typeparams := meth.Type().(*types.Signature).TypeParams() | ||
| populateTypeParamParents(typeparams, originmeth, false) | ||
|
|
||
| origintypeparams := originmeth.Type().(*types.Signature).TypeParams() | ||
| populateTypeParamOrigins(meth, typeparams, origintypeparams) | ||
| } | ||
|
|
||
| func populateTypeParamOrigins(meth *types.Func, typeparams *types.TypeParamList, origintypeparams *types.TypeParamList) { | ||
| if typeparams.Len() != origintypeparams.Len() { | ||
| log.Fatalf("Method instantiation %s has %d type parameters, origin has %d", | ||
| meth, typeparams.Len(), origintypeparams.Len()) | ||
| } | ||
|
|
||
| for j := 0; j < typeparams.Len(); j++ { | ||
| setTypeParamOrigin(typeparams.At(j), origintypeparams.At(j)) | ||
| } | ||
| } | ||
|
|
||
| func setTypeParamOrigin(typeparam *types.TypeParam, origintypeparam *types.TypeParam) { | ||
| typeParamOriginMutex.Lock() | ||
| defer typeParamOriginMutex.Unlock() | ||
|
|
||
| entry, exists := typeParamOrigin[typeparam] | ||
| if !exists { | ||
| typeParamOrigin[typeparam] = origintypeparam | ||
| } else if entry != origintypeparam { | ||
| log.Fatalf("Origin of type parameter '%s %s' being set to a different value: '%s' vs '%s'", | ||
| typeparam.String(), typeparam.Constraint().String(), entry.String(), origintypeparam.String()) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shipwright · HIGH
The new global maps typeParamParent and typeParamOrigin are protected by separate mutexes, but getTypeParamParentLabel and getTypeParamOrigin each acquire only their own lock.
Impact: The new global maps typeParamParent and typeParamOrigin are protected by separate mutexes, but getTypeParamParentLabel and getTypeParamOrigin each acquire only their own lock. If any code path ever reads or writes both maps in a way that requires a consistent cross-map view, the separate locks provide no atomicity. More concretely, setTypeParamParent and setTypeParamOrigin are called from populateTypeParamParentsAnd…
Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.