203 lines
7.0 KiB
Alloy
203 lines
7.0 KiB
Alloy
enum Flag {Method, MethodOrField, Public}
|
|
|
|
/* There is a negative version for each flag (METHOD and NOT_METHOD).
|
|
Model this as two sets, one of positive flags, and one of netative flags,
|
|
and interpret the bitfield to be a conjunction of both flags. */
|
|
sig Bitfield {
|
|
, positiveFlags: set Flag
|
|
, negativeFlags: set Flag
|
|
}
|
|
|
|
/* A filter state has filterFlags and excludeFlags, both represented as conjunctions. */
|
|
sig FilterState {
|
|
, curFilter: Bitfield
|
|
}
|
|
|
|
/* Initially, no search has happeneed for a scope, so its 'found' is not set to anything. */
|
|
one sig NotSet {}
|
|
|
|
/* Finally, there's a search state (whether or not a particular scope has already been
|
|
searched with a particular configuration). */
|
|
one sig SearchState {
|
|
, var found: Bitfield + NotSet
|
|
}
|
|
|
|
|
|
pred bitfieldEmpty[b: Bitfield] {
|
|
#b.positiveFlags = 0 and #b.negativeFlags = 0
|
|
}
|
|
|
|
pred bitfieldEqual[b1: Bitfield, b2: Bitfield] {
|
|
b1.positiveFlags = b2.positiveFlags and b1.negativeFlags = b2.negativeFlags
|
|
}
|
|
|
|
pred bitfieldIntersection[b1: Bitfield, b2: Bitfield, b3: Bitfield] {
|
|
b3.positiveFlags = b1.positiveFlags & b2.positiveFlags
|
|
b3.negativeFlags = b1.negativeFlags & b2.negativeFlags
|
|
}
|
|
|
|
pred bitfieldSubset[b1: Bitfield, b2: Bitfield] {
|
|
b1.positiveFlags in b2.positiveFlags
|
|
b1.negativeFlags in b2.negativeFlags
|
|
}
|
|
|
|
pred bitfieldIncomparable[b1: Bitfield, b2: Bitfield] {
|
|
not bitfieldSubset[b1, b2]
|
|
not bitfieldSubset[b2, b1]
|
|
}
|
|
|
|
pred addBitfieldFlag[b1: Bitfield, b2: Bitfield, flag: Flag] {
|
|
b2.positiveFlags = b1.positiveFlags + flag
|
|
b2.negativeFlags = b1.negativeFlags
|
|
}
|
|
|
|
pred addBitfieldFlagNeg[b1: Bitfield, b2: Bitfield, flag: Flag] {
|
|
b2.negativeFlags = b1.negativeFlags + flag
|
|
b2.positiveFlags = b1.positiveFlags
|
|
}
|
|
|
|
enum Property { PMethod, PField, PPublic }
|
|
|
|
sig Symbol {
|
|
properties: set Property
|
|
}
|
|
|
|
pred flagMatchesProperty[flag: Flag, property: Property] {
|
|
(flag = Method and property = PMethod) or
|
|
(flag = MethodOrField and (property = PMethod or property = PField)) or
|
|
(flag = Public and property = PPublic)
|
|
}
|
|
|
|
pred bitfieldMatchesProperties[bitfield: Bitfield, symbol: Symbol] {
|
|
all flag: bitfield.positiveFlags | some property: symbol.properties | flagMatchesProperty[flag, property]
|
|
all flag: bitfield.negativeFlags | no property: symbol.properties | flagMatchesProperty[flag, property]
|
|
}
|
|
|
|
bitfieldExists: run {
|
|
some Bitfield
|
|
}
|
|
|
|
matchingBitfieldExists: run {
|
|
some bitfield : Bitfield, symbol : Symbol | bitfieldMatchesProperties[bitfield, symbol]
|
|
}
|
|
|
|
matchingBitfieldExists2: run {
|
|
some bitfield : Bitfield, symbol : Symbol {
|
|
#bitfield.positiveFlags = 1
|
|
#bitfield.negativeFlags = 1
|
|
#symbol.properties = 2
|
|
bitfieldMatchesProperties[bitfield, symbol]
|
|
}
|
|
}
|
|
|
|
fact "method and field are incompatible" {
|
|
always no symbol: Symbol | {
|
|
PMethod in symbol.properties and PField in symbol.properties
|
|
}
|
|
}
|
|
|
|
fact "public and field are incompatible" {
|
|
always no symbol: Symbol | {
|
|
PPublic in symbol.properties and PField in symbol.properties
|
|
}
|
|
}
|
|
|
|
matchingBitfieldExists3: run {
|
|
some bitfield : Bitfield, symbol : Symbol {
|
|
#bitfield.positiveFlags = 2
|
|
#symbol.properties = 2
|
|
bitfieldMatchesProperties[bitfield, symbol]
|
|
}
|
|
}
|
|
|
|
pred possibleState[filterState: FilterState] {
|
|
some initialState: FilterState {
|
|
// Each lookup in scope starts with empty filter flags
|
|
bitfieldEmpty[initialState.curFilter]
|
|
|
|
// The intermediate states (bitfieldMiddle) are used for sequencing of operations.
|
|
some bitfieldMiddle : Bitfield {
|
|
// Add "Public" depending on skipPrivateVisibilities
|
|
addBitfieldFlag[initialState.curFilter, bitfieldMiddle, Public] or
|
|
bitfieldEqual[initialState.curFilter, bitfieldMiddle]
|
|
|
|
// If it's a method receiver, add method or field restriction
|
|
addBitfieldFlag[bitfieldMiddle, filterState.curFilter, MethodOrField] or
|
|
// if it's not a receiver, filter to non-methods (could be overridden)
|
|
// addBitfieldFlagNeg[bitfieldMiddle, filterState.curFilter, Method] or
|
|
// Maybe methods are not being curFilterd but it's not a receiver, so no change.
|
|
bitfieldEqual[bitfieldMiddle, filterState.curFilter]
|
|
}
|
|
}
|
|
}
|
|
|
|
possibleStateExists: run {
|
|
some filterState : FilterState | possibleState[filterState] and #filterState.curFilter.positiveFlags = 1
|
|
}
|
|
|
|
pred update[toSet: Bitfield + NotSet, setTo: FilterState] {
|
|
toSet' in Bitfield and bitfieldIntersection[toSet, setTo.curFilter, toSet']
|
|
}
|
|
|
|
pred newUpdate[toSet: Bitfield + NotSet, setTo: FilterState] {
|
|
(not bitfieldIncomparable[toSet, setTo.curFilter] and update[toSet, setTo]) or
|
|
(bitfieldIncomparable[toSet, setTo.curFilter] and toSet = toSet')
|
|
}
|
|
|
|
pred updateOrSet[toSet: Bitfield + NotSet, setTo: FilterState] {
|
|
(toSet in NotSet and toSet' = setTo.curFilter) or
|
|
(toSet not in NotSet and update[toSet, setTo])
|
|
}
|
|
|
|
pred excludeBitfield[found: Bitfield + NotSet, exclude: Bitfield] {
|
|
(found != NotSet and bitfieldEqual[found, exclude]) or
|
|
(found = NotSet and bitfieldEmpty[exclude])
|
|
}
|
|
|
|
fact init {
|
|
all searchState: SearchState | searchState.found = NotSet
|
|
}
|
|
|
|
fact step {
|
|
always {
|
|
// Model that a new doLookupInScope could've occurred, with any combination of flags.
|
|
all searchState: SearchState {
|
|
some fs: FilterState {
|
|
// This is a possible combination of lookup flags
|
|
possibleState[fs]
|
|
|
|
// If a search has been performed before, take the intersection; otherwise,
|
|
// just insert the current filter flags.
|
|
updateOrSet[searchState.found, fs]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
counterexampleNotFound: run {
|
|
all searchState: SearchState {
|
|
// a way that subsequent results of searching will miss things.
|
|
eventually some symbol: Symbol,
|
|
fs: FilterState, fsBroken: FilterState,
|
|
exclude1: Bitfield, exclude2: Bitfield {
|
|
// Some search (fs) will cause a transition / modification of the search state...
|
|
possibleState[fs]
|
|
updateOrSet[searchState.found, fs]
|
|
excludeBitfield[searchState.found, exclude1]
|
|
// Such that a later, valid search... (fsBroken)
|
|
possibleState[fsBroken]
|
|
excludeBitfield[searchState.found', exclude2]
|
|
|
|
// Will allow for a symbol ...
|
|
// ... that are left out of the original search...
|
|
not bitfieldMatchesProperties[searchState.found, symbol]
|
|
// ... and out of the current search
|
|
not (bitfieldMatchesProperties[fs.curFilter, symbol] and not bitfieldMatchesProperties[exclude1, symbol])
|
|
// But would be matched by the broken search...
|
|
bitfieldMatchesProperties[fsBroken.curFilter, symbol]
|
|
// ... to not be matched by a search with the new state:
|
|
not (bitfieldMatchesProperties[fsBroken.curFilter, symbol] and not bitfieldMatchesProperties[exclude2, symbol])
|
|
}
|
|
}
|
|
}
|