diff --git a/docs/mooseide-integration.md b/docs/mooseide-integration.md index 84917fe..e38626d 100644 --- a/docs/mooseide-integration.md +++ b/docs/mooseide-integration.md @@ -22,3 +22,22 @@ fork-first v3 development work, not a published v3 release. GitHub CI loads these optional tests in the ready-made Moose image. The plain Pharo job continues to test Core without GUI dependencies. + +## Dead-code candidates + +Use `FmxSQLDeadCodeRule openOn: routines` to open the standard Dead Code browser +with the SQL refuting rule selected, or select that rule explicitly in an existing +browser before computing. SQL routines are externally callable, so this SQL preset +protects all of them until a closed-world candidate set is explicitly supplied. +The generic OO preset alone is not a SQL dead-code analysis. +Trigger/constraint bindings and missing or incomplete analysis remain protected. +For an explicitly bounded analysis, configure its selected `FmxSQLDeadCodeRule`: + +```smalltalk +rule closedWorldCandidates: knownClosedWorldRoutines. +rule externalEntryPoints: knownExternalRoots. +``` + +The remaining results are candidates, not proof that routines can be dropped. +This integration does not execute database DDL or remove definitions. Existing +non-SQL dead-code rules retain their behavior. diff --git a/src/FamixNGSQL-MooseIDE-Tests/FmxSQLDeadCodeTest.class.st b/src/FamixNGSQL-MooseIDE-Tests/FmxSQLDeadCodeTest.class.st new file mode 100644 index 0000000..f79362c --- /dev/null +++ b/src/FamixNGSQL-MooseIDE-Tests/FmxSQLDeadCodeTest.class.st @@ -0,0 +1,54 @@ +Class { + #name : #FmxSQLDeadCodeTest, + #superclass : #TestCase, + #category : #'FamixNGSQL-MooseIDE-Tests' +} +{ #category : #tests } +FmxSQLDeadCodeTest >> testDefaultProtectsExternalAndUncertainRoutines [ + | routine rule | + routine := FmxSQLStoredProcedure new. + rule := FmxSQLDeadCodeRule new. + self deny: rule selectedByDefault. + self assert: (rule notDead: routine). + rule closedWorldCandidates: { routine }. + self assert: (rule notDead: routine). + routine cacheAt: #analysisStatus put: #visited. + self deny: (rule notDead: routine). + rule externalEntryPoints: { routine }. + self assert: (rule notDead: routine). + self deny: (rule notDead: FamixStMethod new) +] +{ #category : #tests } +FmxSQLDeadCodeTest >> testTriggerBindingProtectsClosedWorldCandidate [ + | routine rule | + routine := FmxSQLTriggerStoredProcedure new. + routine cacheAt: #analysisStatus put: #visited. + FmxSQLTrigger new storedProcedure: routine. + rule := FmxSQLDeadCodeRule new closedWorldCandidates: { routine }; yourself. + self assert: (rule notDead: routine) +] +{ #category : #tests } +FmxSQLDeadCodeTest >> testConstraintBindingProtectsClosedWorldCandidate [ + | routine rule | + routine := FmxSQLStoredProcedure new. + routine cacheAt: #analysisStatus put: #visited. + FmxSQLCheckConstraint new addStoredProceduresCalled: routine. + rule := FmxSQLDeadCodeRule new closedWorldCandidates: { routine }; yourself. + self assert: (rule notDead: routine) +] +{ #category : #tests } +FmxSQLDeadCodeTest >> testStandardBrowserUsesConservativeDefault [ + | model routine browser rule | + model := FmxSQLModel new. + routine := FmxSQLStoredProcedure new name: 'candidate'; mooseModel: model; yourself. + routine cacheAt: #analysisStatus put: #visited. + browser := FmxSQLDeadCodeRule openOn: { routine }. + [ + browser specModel runComputation. + self assertEmpty: browser specModel deadEntities. + rule := browser specModel chosenRules detect: [ :each | each isKindOf: FmxSQLDeadCodeRule ]. + rule closedWorldCandidates: { routine }. + browser specModel runComputation. + self assert: (browser specModel deadEntities includes: routine) ] + ensure: [ browser window ifNotNil: #close ] +] diff --git a/src/FamixNGSQL-MooseIDE/FmxSQLDeadCodeRule.class.st b/src/FamixNGSQL-MooseIDE/FmxSQLDeadCodeRule.class.st new file mode 100644 index 0000000..a4ae8d4 --- /dev/null +++ b/src/FamixNGSQL-MooseIDE/FmxSQLDeadCodeRule.class.st @@ -0,0 +1,44 @@ +"SQL routines are externally callable. The default protects them all. +Opt into a closed-world candidate set only when its external callers are known. +Trigger/constraint entry points and incomplete analysis remain protected." +Class { + #name : #FmxSQLDeadCodeRule, + #superclass : #MiDeadAbstractRule, + #instVars : [ 'closedWorldCandidates', 'externalEntryPoints' ], + #category : #'FamixNGSQL-MooseIDE' +} +{ #category : #initialization } +FmxSQLDeadCodeRule >> initialize [ + super initialize. + closedWorldCandidates := IdentitySet new. + externalEntryPoints := IdentitySet new +] +{ #category : #accessing } +FmxSQLDeadCodeRule >> closedWorldCandidates: entities [ closedWorldCandidates := entities asIdentitySet ] +{ #category : #accessing } +FmxSQLDeadCodeRule >> externalEntryPoints: entities [ externalEntryPoints := entities asIdentitySet ] +{ #category : #accessing } +FmxSQLDeadCodeRule >> name [ ^ super name, 'PostgreSQL entry points and incomplete evidence' ] +{ #category : #testing } +FmxSQLDeadCodeRule >> selectedByDefault [ ^ false ] +{ #category : #testing } +FmxSQLDeadCodeRule >> notDead: entity [ + (entity isKindOf: FmxSQLStoredProcedure) ifFalse: [ ^ false ]. + (closedWorldCandidates includes: entity) ifFalse: [ ^ true ]. + (externalEntryPoints includes: entity) ifTrue: [ ^ true ]. + ((entity respondsTo: #triggers) and: [ entity triggers isNotEmpty ]) ifTrue: [ ^ true ]. + (entity checkConstraintsCalling isNotEmpty or: [ entity exclusionConstraintsCalling isNotEmpty ]) ifTrue: [ ^ true ]. + ^ (entity cacheAt: #analysisStatus ifAbsent: [ nil ]) ~= #visited +] + +{ #category : #opening } +FmxSQLDeadCodeRule class >> openOn: entities [ + | browser | + (entities isEmpty or: [ (entities allSatisfy: [ :entity | entity isKindOf: FmxSQLStoredProcedure ]) not ]) + ifTrue: [ self error: 'SQL dead-code analysis expects a nonempty routine collection' ]. + browser := MiDeadBrowser new. + browser open. + browser specModel chooseRules: { self new }. + browser followEntity: entities asMooseGroup. + ^ browser +]