From VisualWorks®, 7.6 of February 8, 2008 on March 3, 2008 at 9:43:39 pm (Dialog confirm: 'You are filing-in a Parcel source file!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned. Choose terminate or close.'] <class> <name>LintRule</name> <environment>Refactory.Browser</environment> <super>Core.Object</super> <private>false</private> none name <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.LintRule
LintRule is an abstract class that represents some check on the code. Subclasses must implement the following messages: accessing problemCount "returns the number of problems this rule found" private viewResults "opens an interface to view the problems" testing isEmpty "did this rule find any problems" Instance Variables: name <String> the display name of the rule
</comment> <class> <name>BasicLintRule</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.LintRule</super> <private>false</private> none result openSymbol <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.BasicLintRule
BasicLintRule represents a check on the code. Instance Variables: openSymbol <ByteSymbol> what is performed when we are opening the results result <ParseTreeEnvironment> some object that represents the results found (it must understand openEditor, isEmpty, problemCount, and copyEmpty). Most of the time, this is a ParseTreeEnvironment object. Shared Variables: FilterDictionary <Dictionary> a dictionary that maps the name of lint rule to the filter environment
</comment> <class> <name>BlockLintRule</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.BasicLintRule</super> <private>false</private> none classBlock methodBlock <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.BlockLintRule
BlockLintRule is a lint rule that runs arbitrary checks. These checks are represented by blocks. This allows one to represent virtually any type of check. Instance Variables: classBlock <BlockClosure> a block that is run when we check a class methodBlock <BlockClosure> a block that is run when we check a method
</comment> <class> <name>SmalllintFilterEnvironment</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.AndEnvironment</super> <private>false</private> none lintRuleName <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.SmalllintFilterEnvironment
SmalllintFilterEnvironment is a special environment that is used to filter out invalid Smalllint results. Instance Variables: lintRuleName <String> the name of the lint rule that we are looking at
</comment> <class> <name>TransformationRule</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.LintRule</super> <private>false</private> none changes <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.TransformationRule
TransformationRule is a LintRule that transforms code. While it doesn't have any subclassResponsibility methods, it is really abstract. Instance Variables: changes <CompositeRefactoryChange> the changes we've made
</comment> <class> <name>SubclassResponsibilityEnvironment</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.SelectorEnvironment</super> <private>false</private> none <imports></imports> <category>Browser-Environments</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.SubclassResponsibilityEnvironment
SubclassResponsibilityEnvironment is a special selector environment that displays subclassResponsibility methods that haven't been written.
</comment> <class> <name>CompositeLintRule</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.LintRule</super> <private>false</private> none rules <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.CompositeLintRule
CompositeLintRule groups LintRules into a single rule (composite pattern). Instance Variables: rules <Collection of: LintRule> the rules that make up the composite
</comment> <class> <name>SmalllintChecker</name> <environment>Refactory.Browser</environment> <super>Core.Object</super> <private>false</private> none rule environment context methodBlock <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.SmalllintChecker
SmalllintChecker represents the process that checks the lint rule over the selected code items (environment). It contains no GUI items and can be run in a headless mode. Instance Variables: context <SmalllintContext> contains our cache as well as the item that we are currently checking environment <BrowserEnvironment> the items that we are to run the checker over methodBlock <BlockClosure> a block that is executed after each method is checked. This is used for updating the % done bar. rule <LintRule> the lint rule that we are checking
</comment> <class> <name>ParseTreeEnvironment</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.SelectorEnvironment</super> <private>false</private> none matcher <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.ParseTreeEnvironment
ParseTreeEnvironment is a special browser environment that causes the code tools to highlight the parse node causing the lint rule to fail. Instance Variables: matcher <ParseTreeSearcher> used to return the parse node that is causing the failure
</comment> <class> <name>ParseTreeLintRule</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.BasicLintRule</super> <private>false</private> none matcher <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.ParseTreeLintRule
ParseTreeLintRule is a lint rule that matches parse trees. When a match is found that creates a problem result. Instance Variables: matcher <ParseTreeSearcher> used to find parse tree nodes that are causing the problem
</comment> <class> <name>MultiEnvironment</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.BrowserEnvironmentWrapper</super> <private>false</private> none environmentDictionaries <imports></imports> <category>Browser-Environments</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.MultiEnvironment
MultiEnvironment is a special environment for the "Method defined in all subclasses, but not in superclass" lint rule. It's basically a hack to get a browser to display the results. Instance Variables: environmentDictionaries <Dictionary key: String value: BrowserEnvironment> the individual results from each failure. Each value stores the methods that are defined in the subclasses
</comment> <class> <name>SmalllintContext</name> <environment>Refactory.Browser</environment> <super>Core.Object</super> <private>false</private> none class selector parseTree literals literalSemaphore literalProcess selectors compiledMethod selfMessages superMessages messages nameSpace <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.SmalllintContext
SmalllintContext represents the current state of the SmalllintChecker. It contains the current item that is being checked (a compiled method or class). It also caches some items so that the checker process can run faster. Instance Variables: class <ClassDescription> the class that is currently being checked compiledMethod <CompiledMethod> the method that is being checked literalProcess <Process> the process that is filling our caches literals <Collection> all literals referenced in all methods literalSemaphore <Semaphore> used to limit access to the literals collection until the literalProcess is finished messages <Collection> all sent messages in the current method namespace <NameSpace> the namespace that contains the class parseTree <RBProgramNode> the parse tree for the method selector <Symbol> the method name selectors <Collection> all implemented selectors in the system selfMessages <Collection> all self messages in the current method superMessages <Collection> all super messages in the current method
</comment> <class> <name>ParseTreeTransformationRule</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.TransformationRule</super> <private>false</private> none class rewriteRule <imports></imports> <category>Browser-Lint</category> <attributes> <package>Browser-Smalllint</package> </attributes> </class> <comment> Refactory.Browser.ParseTreeTransformationRule
ParseTreeTransformationRule is a LintRule that transforms code using parse tree rewrite rules. Instance Variables: class <Behavior> the class we're looking at currently (only needed so we don't have to pass the class around) rewriteRule <ParseTreeRewriter> the rule we're replacing the code with Shared Variables: RecursiveSelfRule <ParseTreeSearcher> a rule that checks our rewrites so we don't make directly recursive methods (e.g., "foo ^self foo")
</comment> <name>FilterDictionary</name> <environment>Refactory.Browser.BasicLintRule</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>Browser-Smalllint</package> </attributes> <name>RecursiveSelfRule</name> <environment>Refactory.Browser.ParseTreeTransformationRule</environment> <private>false</private> <constant>false</constant> <category>As yet unclassified</category> <attributes> <package>Browser-Smalllint</package> </attributes> <methods> Refactory.Browser.LintRule <category>accessing</category>
allRules | allRules | allRules := OrderedCollection withAll: self rules. self rules do: [:each | allRules addAll: each allRules]. ^allRules
checkClass: aSmalllintContext
checkMethod: aSmalllintContext
name ^name
name: aString name := aString
problemCount ^self subclassResponsibility
rules ^#()
</methods> <methods> Refactory.Browser.LintRule <category>testing</category>
hasConflicts ^false
isComposite ^false
isEmpty self subclassResponsibility
notEmpty ^self isEmpty not
</methods> <methods> Refactory.Browser.LintRule <category>initialize-release</category>
initialize
resetResult
</methods> <methods> Refactory.Browser.LintRule <category>printing</category>
displayName | nameStream | nameStream := WriteStream on: (String new: 64). nameStream nextPutAll: self name asString; nextPutAll: ' ['. self problemCount printOn: nameStream. nameStream nextPut: $]. ^nameStream contents
printOn: aStream name isNil ifTrue: [super printOn: aStream] ifFalse: [aStream nextPutAll: name asString]
</methods> <methods> Refactory.Browser.LintRule <category>private</category>
failedRules ^self isEmpty ifTrue: [#()] ifFalse: [Array with: self]
</methods> <methods> Refactory.Browser.LintRule class <category>instance creation</category>
new ^super new initialize
</methods> <methods> Refactory.Browser.BasicLintRule <category>private</category>
defaultResultClass ^SelectorEnvironment
</methods> <methods> Refactory.Browser.BasicLintRule <category>accessing</category>
filteredResult ^(SmalllintFilterEnvironment onEnvironment: result and: (self class filterDictionary at: self name ifAbsent: [result copyEmpty]) copy not) label: result label; lintRuleName: self name; yourself
problemCount ^self result problemCount
result ^(self class filterDictionary includesKey: self name) ifTrue: [self filteredResult] ifFalse: [result]
</methods> <methods> Refactory.Browser.BasicLintRule <category>initialize-release</category>
initialize super initialize. openSymbol := #openWithFilters. self resultClass: self defaultResultClass
openUsing: aSymbol openSymbol := aSymbol
resetResult result := result copyEmpty. result label: name
result: aResult result := aResult copyEmpty
resultClass: aClass result := aClass new
</methods> <methods> Refactory.Browser.BasicLintRule <category>testing</category>
isEmpty ^self result isEmpty
</methods> <methods> Refactory.Browser.BasicLintRule class <category>accessing</category>
addFilter: anEnvironment for: aString self filterDictionary at: aString put: anEnvironment copy
filterDictionary ^FilterDictionary isNil ifTrue: [FilterDictionary := Dictionary new] ifFalse: [FilterDictionary]
filterDictionary: aDictionary FilterDictionary := aDictionary
filterFor: aName ^self filterDictionary at: aName ifAbsentPut: [SelectorEnvironment new]
protocols ^ OrderedCollection with: ( #Bugs << #browser >> 'Bugs' ) -> 'bugs' with: ( #PossibleBugs << #browser >> 'Possible bugs' ) -> 'possible bugs' with: ( #UnnecessaryCode << #browser >> 'Unnecessary code' ) -> 'unnecessary code' with: ( #IntentionRevealing << #browser >> 'Intention revealing' ) -> 'intention revealing'.
</methods> <methods> Refactory.Browser.BasicLintRule class <category>storing</category>
storeFiltersOn: aStream aStream nextPut: $(; nextPutAll: self fullName; nextPutAll: ' filterDictionary: ('; nextPutAll: Dictionary fullName; nextPutAll: ' new'. self filterDictionary keysAndValuesDo: [:key :value | aStream nextPutAll: ' at: '. key storeOn: aStream. aStream nextPutAll: ' put: '. value storeOn: aStream. aStream nextPutAll: ';'; cr]. aStream tab; nextPutAll: 'yourself))'
</methods> <methods> Refactory.Browser.BlockLintRule <category>accessing</category>
checkClass: aSmalllintContext ^classBlock value: aSmalllintContext value: result
checkMethod: aSmalllintContext ^methodBlock value: aSmalllintContext value: result
</methods> <methods> Refactory.Browser.BlockLintRule <category>initialize-release</category>
classBlock: aBlock classBlock := aBlock
initialize super initialize. classBlock := [:context :aResult | ]. methodBlock := [:context :aResult | ]
methodBlock: aBlock methodBlock := aBlock
</methods> <methods> Refactory.Browser.BlockLintRule class <category>possible bugs</category>
abstractClass | detector subclassResponsibilitySymbol | detector := self new. detector name: (#ReferencesAnAbstractClass << #browser >> 'References an abstract class') asString. detector resultClass: ClassEnvironment. subclassResponsibilitySymbol := 'subclassResponsibility' asSymbol. detector classBlock: [:context :result | (context selectedClass whichSelectorsReferTo: subclassResponsibilitySymbol) notEmpty ifTrue: [(context uses: context selectedClass) ifTrue: [result addClass: context selectedClass]]]. ^detector
addRemoveDependents | detector | detector := self new. detector resultClass: ClassEnvironment. detector name: (#AddRemoveDependentMessageMismatch << #browser >> 'Number of addDependent: messages > removeDependent:') asString. detector classBlock: [:context :result | | count | count := 0. ((Set withAll: (context selectedClass whichSelectorsReferTo: #addDependent:)) addAll: (context selectedClass whichSelectorsReferTo: #removeDependent:); yourself) do: [:sel | (context selectedClass compiledMethodAt: sel) messagesDo: [:each | each == #addDependent: ifTrue: [count := count + 1]. each == #removeDependent: ifTrue: [count := count - 1]]]. count > 0 ifTrue: [result addClass: context selectedClass]]. ^detector
classInstVarNotInitialized | detector | detector := self new. detector name: (#HasClassInstVarButNoInitialize << #browser >> 'Has class instance variables but no initialize method') asString. detector resultClass: ClassEnvironment. detector classBlock: [:context :result | context selectedClass isMeta ifTrue: [(context selectedClass instVarNames notEmpty and: [(context selectedClass includesSelector: #initialize) not]) ifTrue: [result addClass: context selectedClass]]]. ^detector
collectionCopyEmpty | detector | detector := self new. detector name: (#SubclassDoesNotDefineCopyEmpty << #browser >> 'Subclass of collection that has instance variable but doesn''t define copyEmpty') asString. detector resultClass: ClassEnvironment. detector classBlock: [:context :result | (context selectedClass isVariable and: [(context selectedClass includesSelector: #copyEmpty:) not and: [context selectedClass instVarNames notEmpty and: [context selectedClass inheritsFrom: Collection]]]) ifTrue: [result addClass: context selectedClass]]. ^detector
definesEqualNotHash | detector | detector := self new. detector name: (#DefinesEqButNotHash << #browser >> 'Defines = but not hash') asString. detector resultClass: ClassEnvironment. detector classBlock: [:context :result | ((context selectedClass includesSelector: #=) and: [(context selectedClass includesSelector: #hash) not]) ifTrue: [result addClass: context selectedClass]]. ^detector
literalArrayContainsComma | detector | detector := self new. detector name: (#LiteralArrayContainsComma << #browser >> 'Literal array contains a #,') asString. detector methodBlock: [:context :result | (context compiledMethod allLiterals anySatisfy: [:each | self doesLiteralArrayContainComma: each]) ifTrue: [result addClass: context selectedClass selector: context selector]]. ^detector
refersToClass | detector | detector := self new. detector name: (#RefersToClassName << #browser >> 'Refers to class name instead of "self class"') asString. detector classBlock: [:context :result | | sels className nonMeta | className := context selectedClass instanceBehavior name. nonMeta := context selectedClass instanceBehavior. sels := context selectedClass whichSelectorsReferTo: (nonMeta environment bindingFor: nonMeta name). sels do: [:each | result addClass: context selectedClass selector: each]. sels notEmpty ifTrue: [result addSearchString: className]]. ^detector
returnsBooleanAndOther | detector matcher | detector := self new. detector name: (#ReturnsABooleanAndNot << #browser >> 'Returns a boolean and non boolean') asString. matcher := ParseTreeSearcher new. matcher matches: '^``@xObject' do: [:aNode :answer | answer add: aNode value; yourself]. detector methodBlock: [:context :result | | hasBool hasSelf | hasBool := false. hasSelf := context parseTree lastIsReturn not. (matcher executeTree: context parseTree initialAnswer: Set new) do: [:each | hasBool := hasBool or: [(each isLiteralNode and: [#(true false) includes: each value]) or: [each isMessage and: [#(#and: #or:) includes: each selector]]]. hasSelf := hasSelf or: [(each isVariable and: [each name = 'self']) or: [each isLiteralNode and: [(#(true false) includes: each value) not]]]]. hasSelf & hasBool ifTrue: [result addClass: context selectedClass selector: context selector]]. ^detector
sendsDifferentSuper | detector | detector := self new. detector name: (#SendsDifferentSuperMessage << #browser >> 'Sends different super message') asString. detector methodBlock: [:context :result | | message | (message := context superMessages detect: [:each | each ~= context selector] ifNone: [nil]) notNil ifTrue: [result addSearchString: message. result addClass: context selectedClass selector: context selector]]. ^detector
superSends | detector | detector := self new. detector name: (#MissingSuperSends << #browser >> 'Missing super sends') asString. detector methodBlock: [:context :result | (context selectedClass isMeta not and: [self superMessages includes: context selector]) ifTrue: [(context selectedClass superclass notNil and: [context selectedClass superclass canUnderstand: context selector]) ifTrue: [(context superMessages includes: context selector) ifFalse: [result addClass: context selectedClass selector: context selector]]]]. ^detector
tempsReadBeforeWritten | detector | detector := self new. detector name: (#TempsReadBeforeWritten << #browser >> 'Temporaries read before written') asString. detector methodBlock: [:context :result | (RBReadBeforeWrittenTester variablesReadBeforeWrittenIn: context parseTree) do: [:each | result addClass: context selectedClass selector: context selector. result addSearchString: each]]. ^detector
tempVarOverridesInstVar | detector matcher vars varName | detector := self new. detector name: (#InstVarOverriddenByTemp << #browser >> 'Instance variable overridden by temporary variable') asString. matcher := (ParseTreeSearcher new) matchesArgument: '`xxxvar' do: [:aNode :answer | answer or: [varName := aNode name. vars includes: varName]]; yourself. detector methodBlock: [:context :result | vars := context instVarNames. (matcher executeTree: context parseTree initialAnswer: false) ifTrue: [result addClass: context selectedClass selector: context selector. result addSearchString: varName]]. ^detector
usesTrue | detector trueBinding falseBinding | detector := self new. trueBinding := self environment bindingFor: #True. falseBinding := self environment bindingFor: #False. detector name: (#UsesBooleanClasses << #browser >> 'Uses True/False instead of true/false') asString. detector methodBlock: [:context :result | | method | method := context compiledMethod. ((method refersToLiteral: trueBinding) or: [method refersToLiteral: falseBinding]) ifTrue: [result addClass: context selectedClass selector: context selector. result searchStrings: #('True' 'False')]]. ^detector
variableNotDefined | detector | detector := self new. detector name: (#VarUsedButNotDefined << #browser >> 'Variable used, but not defined anywhere') asString. detector methodBlock: [:context :result | context compiledMethod withAllBlockMethodsDo: [:each | each literalsDo: [:lit | | binding | lit isVariableBinding ifTrue: [(((binding := context selectedClass bindingFor: lit key) notNil and: [binding binding == lit binding]) or: [((binding := context selectedClass environment bindingFor: lit key) notNil and: [binding binding == lit binding]) or: [(binding := Undeclared bindingFor: lit key) notNil and: [binding binding == lit binding]]]) ifFalse: [result addClass: context selectedClass selector: context selector. result addSearchString: lit key]]]]]. ^detector
</methods> <methods> Refactory.Browser.BlockLintRule class <category>unnecessary code</category>
classNotReferenced | detector | detector := self new. detector name: (#ClassNotReferenced << #browser >> 'Class not referenced') asString. detector resultClass: ClassEnvironment. detector classBlock: [:context :result | (context selectedClass isMeta or: [context selectedClass subclasses notEmpty]) ifFalse: [((context uses: context selectedClass) or: [context uses: context selectedClass name]) ifFalse: [result addClass: context selectedClass; addClass: context selectedClass class]]]. ^detector
equivalentSuperclassMethods | detector | detector := self new. detector name: (#IdenticalMethodsInSuperclass << #browser >> 'Methods equivalently defined in superclass') asString. detector methodBlock: [:context :result | context selectedClass superclass notNil ifTrue: [(context selectedClass superclass canUnderstand: context selector) ifTrue: [(((context selectedClass superclass whichClassIncludesSelector: context selector) compiledMethodAt: context selector) equivalentTo: context compiledMethod) ifTrue: [result addClass: context selectedClass selector: context selector]]]]. ^detector
implementedNotSent | detector | detector := self new. detector name: (#MethodsImpButNotSent << #browser >> 'Methods implemented but not sent') asString. detector methodBlock: [:context :result | ((context uses: context selector) or: [self isMenuPragmaMethod: context compiledMethod]) ifFalse: [result addClass: context selectedClass selector: context selector]]. ^detector
instVarInSubclasses | detector | detector := self new. detector name: (#InstVarsInAllSubclasses << #browser >> 'Instance variables defined in all subclasses') asString; resultClass: VariableEnvironment; openUsing: #openWithoutFilters. detector classBlock: [:context :result | | subs | subs := context selectedClass subclasses. subs size > 1 ifTrue: [| sels | sels := Bag new. subs do: [:each | sels addAll: each instVarNames]. sels asSet do: [:val | | count | count := sels occurrencesOf: val. count == subs size ifTrue: [result addClass: context selectedClass instanceVariable: val]]]]. ^detector
justSendsSuper | detector matcher | detector := self new. detector name: (#MethodJustSendsSuper << #browser >> 'Method just sends super message') asString. matcher := ParseTreeSearcher justSendsSuper. detector methodBlock: [:context :result | (context parseTree isPrimitive not and: [matcher executeMethod: context parseTree initialAnswer: false]) ifTrue: [result addClass: context selectedClass selector: context selector]]. ^detector
onlyReadOrWritten | detector | detector := self new. detector name: (#InstVarOnlyReadOrWritten << #browser >> 'Instance variables not read AND written') asString; resultClass: VariableEnvironment; openUsing: #openWithoutFilters. detector classBlock: [:context :result | | allSubclasses | allSubclasses := context selectedClass withAllSubclasses. context selectedClass instVarNames do: [:each | | isRead isWritten | isRead := false. isWritten := false. allSubclasses detect: [:class | isRead ifFalse: [isRead := (class whichSelectorsRead: each) notEmpty]. isWritten ifFalse: [isWritten := (class whichSelectorsAssign: each) notEmpty]. isRead & isWritten] ifNone: [result addClass: context selectedClass instanceVariable: each]]]. ^detector
unreferencedVariables | detector | detector := self new. detector name: (#InstVarNotReferenced << #browser >> 'Instance variables not referenced') asString; resultClass: VariableEnvironment; openUsing: #openWithoutFilters. detector classBlock: [:context :result | | allSubclasses | allSubclasses := context selectedClass withAllSubclasses. context selectedClass instVarNames do: [:each | allSubclasses detect: [:class | (class whichSelectorsAccess: each) notEmpty] ifNone: [result addClass: context selectedClass instanceVariable: each]]]. ^detector
variableAssignedLiteral | detector | detector := self new. detector name: (#VariableAssignedSingleLiteral << #browser >> 'Variable is only assigned a single literal value') asString; resultClass: VariableEnvironment; openUsing: #openWithoutFilters. detector classBlock: [:context :result | | allSubclasses | allSubclasses := context selectedClass withAllSubclasses. context selectedClass instVarNames do: [:each | | defClass selector | (allSubclasses inject: 0 into: [:sum :class | | sels | sels := class whichSelectorsAssign: each. sels size == 1 ifTrue: [selector := sels asArray first. defClass := class]. sum + sels size]) == 1 ifTrue: [| tree searcher | searcher := ParseTreeSearcher new. searcher matches: each , ' := ``@object' do: [:aNode :answer | answer isNil and: [aNode value isLiteralNode]]. tree := defClass parseTreeFor: selector. tree notNil ifTrue: [(searcher executeTree: tree initialAnswer: nil) == true ifTrue: [result addClass: context selectedClass instanceVariable: each]]]]]. ^detector
variableReferencedOnce | detector | detector := self new. detector name: (#InstVarReferencedOnced << #browser >> 'Variable referenced in only one method and always assigned first') asString. detector classBlock: [:context :result | | allSubclasses | allSubclasses := context selectedClass withAllSubclasses. context selectedClass instVarNames do: [:each | | defClass selector | (allSubclasses inject: 0 into: [:sum :class | | sels | sels := class whichSelectorsAccess: each. sels size == 1 ifTrue: [selector := sels asArray first. defClass := class]. sum + sels size]) == 1 ifTrue: [| tree | tree := defClass parseTreeFor: selector. tree notNil ifTrue: [(RBReadBeforeWrittenTester isVariable: each writtenBeforeReadIn: tree) ifTrue: [result addClass: defClass selector: selector. result addSearchString: each]]]]]. ^detector
</methods> <methods> Refactory.Browser.BlockLintRule class <category>private</category>
badSelectors ^#(#become: #isKindOf: #changeClassTo: #changeClassToThatOf: #respondsTo: #isMemberOf: #performMethod: #performMethod:arguments: #performMethod:with: #performMethod:with:with: #performMethod:with:with:with: #allOwners #allOwnersWeakly: #firstOwner #instVarAt: #instVarAt:put: #nextInstance #nextObject #ownerAfter: #primBecome: #halt)
classShouldNotOverride ^#(#== #class)
doesLiteralArrayContainComma: aLiteral aLiteral class == Array ifFalse: [^false]. (aLiteral includes: #,) ifTrue: [^true]. ^aLiteral anySatisfy: [:each | self doesLiteralArrayContainComma: each]
isMenuPragmaMethod: aCompiledMethod | pragmas | pragmas := aCompiledMethod attributeMessages. pragmas isNil ifTrue: [^false]. ^pragmas anySatisfy: [:each | self menuPragmas includes: each selector]
longMethodSize ^10
menuPragmas ^MenuAutomaticGenerator organization listAtCategoryNamed: #generating
metaclassShouldNotOverride ^#(#name #comment)
subclassOf: aClass overrides: aSelector ^aClass subclasses anySatisfy: [:each | (each includesSelector: aSelector) or: [self subclassOf: each overrides: aSelector]]
superMessages ^#(#release #postCopy #postBuildWith: #preBuildWith: #postOpenWith: #noticeOfWindowClose: #initialize #obsolete)
utilityProtocols "If a method is defined in one of these protocols, then don't check if its a utility method." ^#('*utilit*')
</methods> <methods> Refactory.Browser.BlockLintRule class <category>intention revealing</category>
classNameInSelector | detector | detector := self new. detector name: (#RedundantClassName << #browser >> 'Redundant class name in selector') asString. detector methodBlock: [:context :result | (context selectedClass isMeta and: [(context selector indexOfSubCollection: context selectedClass instanceBehavior name startingAt: 1) > 0]) ifTrue: [result addClass: context selectedClass selector: context selector]]. ^detector
collectionMessagesToExternalObject | detector matcher smalllintContext | detector := self new. detector name: (#SendsAddRemoveToExternalCollection << #browser >> 'Sends add:/remove: to external collection') asString. matcher := ParseTreeSearcher new. matcher matchesAnyOf: (#(#add: #remove: #addAll: #removeAll:) collect: [:each | ('(`@Object `@message: `@args) <1s> `@Arg' expandMacrosWith: each) asString]) do: [:aNode :answer | answer isNil ifTrue: [((aNode receiver selector copyFrom: 1 to: (aNode receiver selector size min: 2)) ~= 'as' and: [| receiver | receiver := aNode receiver receiver. receiver isVariable not or: [((#('self' 'super') includes: receiver name) or: [(smalllintContext selectedClass environment bindingFor: receiver name asSymbol) notNil]) not]]) ifTrue: [aNode] ifFalse: [nil]] ifFalse: [answer]]. detector methodBlock: [:context :result | smalllintContext := context. ((matcher canMatchMethod: context compiledMethod) and: [(matcher executeTree: context parseTree initialAnswer: nil) notNil]) ifTrue: [result addClass: context selectedClass selector: context selector]]. ^detector
missingSubclassResponsibility | detector | detector := self new. detector name: (#MethodDefinedInSubsButNotSupers << #browser >> 'Method defined in all subclasses, but not in superclass') asString; resultClass: MultiEnvironment; openUsing: #openWithoutFilters. detector classBlock: [:context :result | | subs | subs := context selectedClass subclasses. subs size > 1 & context selectedClass isMeta not ifTrue: [| sels | sels := Bag new. subs do: [:each | sels addAll: each selectors]. sels asSet do: [:each | ((sels occurrencesOf: each) == subs size and: [(context selectedClass canUnderstand: each) not]) ifTrue: [| envName | envName := context selectedClass name , '>>' , each. subs do: [:subClass | result addClass: subClass selector: each into: envName]]]]]. ^detector
</methods> <methods> Refactory.Browser.BlockLintRule class <category>bugs</category>
overridesSpecialMessage | detector | detector := self new. detector name: (#OverridesASpecialMessage << #browser >> 'Overrides a "special" message') asString. detector resultClass: ClassEnvironment. detector classBlock: [:context :result | ((context selectedClass isMeta ifTrue: [self metaclassShouldNotOverride] ifFalse: [self classShouldNotOverride]) anySatisfy: [:each | context selectedClass superclass notNil and: [(context selectedClass superclass canUnderstand: each) and: [context selectedClass includesSelector: each]]]) ifTrue: [result addClass: context selectedClass]]. ^detector
sentNotImplemented | detector | detector := self new. detector name: (#MessagesSentButNotImplemented << #browser >> 'Messages sent but not implemented') asString. detector methodBlock: [:context :result | | message | message := context messages detect: [:each | (context implements: each) not] ifNone: [nil]. message isNil ifTrue: [message := context superMessages detect: [:each | context selectedClass superclass isNil or: [(context selectedClass superclass canUnderstand: each) not]] ifNone: [nil]. message isNil ifTrue: [message := context selfMessages detect: [:each | (context selectedClass canUnderstand: each) not] ifNone: [nil]]]. message notNil ifTrue: [result addSearchString: message. result addClass: context selectedClass selector: context selector]]. ^detector
subclassResponsibilityNotDefined | detector subclassResponsibilitySymbol | detector := self new. detector name: (#NoSubclassResponsibility << #browser >> 'Subclass responsibility not defined') asString; resultClass: SubclassResponsibilityEnvironment; openUsing: #openWithoutFilters. subclassResponsibilitySymbol := CompiledCode specialSelectorIndexFor: #subclassResponsibility. detector classBlock: [:context :result | context selectedClass subclasses isEmpty ifTrue: [context selectedClass allSelectors do: [:each | | impClass | impClass := context selectedClass whichClassIncludesSelector: each. ((impClass compiledMethodAt: each) sendsSpecialSelector: subclassResponsibilitySymbol) ifTrue: [result addClass: context selectedClass selector: each]]]]. ^detector
undeclaredReference | detector | detector := self new. detector name: (#ReferencesUndeclaredVariable << #browser >> 'References an undeclared variable') asString. detector methodBlock: [:context :result | | undeclared | undeclared := Smalltalk.Undeclared localBindings detect: [:each | (context uses: each) and: [context compiledMethod refersToLiteral: each]] ifNone: [nil]. undeclared notNil ifTrue: [result addSearchString: undeclared key. result addClass: context selectedClass selector: context selector]]. ^detector
</methods> <methods> Refactory.Browser.BlockLintRule class <category>deprecated</category>
badMessage | detector badMessages | detector := self new. detector name: (#SendsQuestionableMessage << #browser >> 'Sends "questionable" message') asString. badMessages := self badSelectors. detector classBlock: [:context :result | | selectors | selectors := badMessages inject: Set new into: [:set :each | set addAll: (context selectedClass whichSelectorsReferTo: each); yourself]. selectors do: [:each | result addClass: context selectedClass selector: each]. selectors notEmpty ifTrue: [result searchStrings: badMessages]]. ^detector
fullBlocks | detector | detector := self new. detector name: (#MethodWithFullBlocks << #browser >> 'Method with full blocks') asString. detector methodBlock: [:context :result | context compiledMethod withAllBlockMethodsDo: [:method | method needsHybridFrame ifTrue: [result addClass: context selectedClass selector: context selector]]]. ^detector
longMethods | detector matcher | detector := self new. detector name: (#LongMethods << #browser >> 'Long methods') asString. matcher := ParseTreeSearcher new. matcher matches: '`.Stmt' do: [:aNode :answer | (aNode children inject: answer into: [:sum :each | matcher executeTree: each initialAnswer: sum]) + 1]. detector methodBlock: [:context :result | (matcher executeTree: context parseTree initialAnswer: 0) >= self longMethodSize ifTrue: [result addClass: context selectedClass selector: context selector]]. ^detector
utilityMethods | detector | detector := self new. detector name: (#UtilityMethods << #browser >> 'Utility methods') asString. detector methodBlock: [:context :result | (context selectedClass isMeta | (context selector numArgs == 0) or: [context protocols anySatisfy: [:each | self utilityProtocols anySatisfy: [:protocol | protocol match: each]]]) ifFalse: [(self subclassOf: context selectedClass overrides: context selector) ifFalse: [(context superMessages isEmpty and: [context selfMessages isEmpty]) ifTrue: [(context parseTree isPrimitive or: [(context selectedClass allInstVarNames , context selectedClass allClassVarNames asArray) , #('self') anySatisfy: [:each | context parseTree references: each]]) ifFalse: [result addClass: context selectedClass selector: context selector]]]]]. ^detector
</methods> <methods> Refactory.Browser.SmalllintFilterEnvironment <category>actions</category>
addFilterForClass: aBehavior (BasicLintRule filterFor: lintRuleName) addClass: aBehavior. andedEnvironment environment addClass: aBehavior
addFilterForClass: aBehavior andSelector: aSymbol (BasicLintRule filterFor: lintRuleName) addClass: aBehavior selector: aSymbol. andedEnvironment environment addClass: aBehavior selector: aSymbol
</methods> <methods> Refactory.Browser.SmalllintFilterEnvironment <category>testing</category>
canFilterSelectors ^environment isClassEnvironment not
isForSmalllint ^true
</methods> <methods> Refactory.Browser.SmalllintFilterEnvironment <category>initialize-release</category>
lintRuleName: aString lintRuleName := aString
</methods> <methods> Refactory.Browser.TransformationRule <category>accessing</category>
problemCount ^changes problemCount
resetResult changes := CompositeRefactoryChange named: (#Transformations << #browser >> 'Transformations') asString
</methods> <methods> Refactory.Browser.TransformationRule <category>testing</category>
hasConflicts ^true
isEmpty ^changes changes isEmpty
</methods> <methods> Refactory.Browser.TransformationRule <category>private</category>
viewResults "I reset the result so that we don't fill up memory with methods to compile in the builder." changes inspect. self resetResult
</methods> <methods> Refactory.Browser.SubclassResponsibilityEnvironment <category>testing</category>
useProtocolNavigator ^true
</methods> <methods> Refactory.Browser.SubclassResponsibilityEnvironment <category>accessing</category>
methodDefinitions | defs | defs := OrderedCollection new. self classesDo: [:class | (self privateSelectorsForClass: class) do: [:sel | | impClass | impClass := class whichClassIncludesSelector: sel. impClass notNil ifTrue: [defs add: (MethodDefinition class: class implementor: impClass selector: sel)]]]. ^defs
</methods> <methods> Refactory.Browser.CompositeLintRule <category>accessing</category>
checkClass: aSmalllintContext rules do: [:each | each checkClass: aSmalllintContext. Processor yield]
checkMethod: aSmalllintContext rules do: [:each | each checkMethod: aSmalllintContext. Processor yield]
failedRules ^rules inject: OrderedCollection new into: [:sum :each | sum addAll: each failedRules; yourself]
problemCount ^rules inject: 0 into: [:count :each | count + each problemCount]
rules ^rules
</methods> <methods> Refactory.Browser.CompositeLintRule <category>testing</category>
hasConflicts ^rules anySatisfy: [:each | each hasConflicts]
isComposite ^true
isEmpty ^rules allSatisfy: [:each | each isEmpty]
</methods> <methods> Refactory.Browser.CompositeLintRule <category>initialize-release</category>
initialize super initialize. rules := #()
resetResult rules do: [:each | each resetResult]
rules: aCollection rules := aCollection
</methods> <methods> Refactory.Browser.CompositeLintRule class <category>instance creation</category>
allRules ^self ruleFor: self protocol: 'all checks'
ruleFor: aClass protocol: aProtocol | allRules prot label | aProtocol isString ifTrue: [prot := aProtocol. label := aProtocol asString copy. label at: 1 put: label first asUppercase] ifFalse: [prot := aProtocol value. label := aProtocol key]. allRules := aClass withAllSubclasses inject: OrderedCollection new into: [:sum :each | sum addAll: ((BrowserEnvironment new selectorsFor: prot asSymbol in: each class) collect: [:selector | each perform: selector]); yourself]. ^self rules: (allRules asSortedCollection: [:a :b | a name < b name]) name: label
rules: aCollection ^(self new) rules: aCollection; yourself
rules: aCollection name: aString ^(self new) rules: aCollection; name: aString; yourself
</methods> <methods> Refactory.Browser.CompositeLintRule class <category>all checks</category>
lintChecks ^self rules: (BasicLintRule protocols collect: [:each | self ruleFor: BasicLintRule protocol: each]) name: (#CodeCriticChecks << #browser >> 'Code Critic Checks') asString
transformations ^self ruleFor: TransformationRule protocol: 'transformations'
</methods> <methods> Refactory.Browser.SmalllintChecker <category>private</category>
checkClass: aClass context selectedClass: aClass. (environment definesClass: aClass) ifTrue: [rule checkClass: context]
checkMethodsForClass: aClass ^environment selectorsForClass: aClass do: [:each | context selector: each. rule checkMethod: context. methodBlock value]
</methods> <methods> Refactory.Browser.SmalllintChecker <category>accessing</category>
context: aSmalllintContext context := aSmalllintContext
environment: aBrowserEnvironment environment := aBrowserEnvironment
methodBlock: aBlock methodBlock := aBlock
rule: aLintRule rule := aLintRule
</methods> <methods> Refactory.Browser.SmalllintChecker <category>initialize-release</category>
initialize methodBlock := []. environment := SelectorEnvironment new. context := SmalllintContext newNoCache
release context release. super release
</methods> <methods> Refactory.Browser.SmalllintChecker <category>actions</category>
run rule resetResult. environment classesDo: [:aClass | self checkClass: aClass. self checkMethodsForClass: aClass]
</methods> <methods> Refactory.Browser.SmalllintChecker class <category>instance creation</category>
new ^(super new) initialize; yourself
newWithContext ^(self new) context: SmalllintContext new; yourself
runRule: aLintRule onEnvironment: aBrowserEnvironment (self new) rule: aLintRule; environment: aBrowserEnvironment; run. ^aLintRule
</methods> <methods> Refactory.Browser.ParseTreeEnvironment <category>initialize-release</category>
matcher: aParseTreeSearcher matcher := aParseTreeSearcher
</methods> <methods> Refactory.Browser.ParseTreeLintRule <category>accessing</category>
checkMethod: aSmalllintContext (matcher canMatchMethod: aSmalllintContext compiledMethod) ifFalse: [^self]. (matcher executeTree: aSmalllintContext parseTree initialAnswer: nil) notNil ifTrue: [result addClass: aSmalllintContext selectedClass selector: aSmalllintContext selector]
</methods> <methods> Refactory.Browser.ParseTreeLintRule <category>private</category>
defaultResultClass ^ParseTreeEnvironment
</methods> <methods> Refactory.Browser.ParseTreeLintRule <category>initialize-release</category>
matcher: aParseTreeMatcher matcher := aParseTreeMatcher
resetResult result := ParseTreeEnvironment new. result label: self name; matcher: matcher
</methods> <methods> Refactory.Browser.ParseTreeLintRule class <category>intention revealing</category>
anySatisfy ^self createParseTreeRule: #('(`@object detect: [:`each | | `@temps| `@.Statements] ifNone: [nil]) isNil' '(`@object detect: [:`each | | `@temps| `@.Statements] ifNone: [nil]) notNil' '(`@object detect: [:`each | | `@temps| `@.Statements] ifNone: [nil]) = nil' '(`@object detect: [:`each | | `@temps| `@.Statements] ifNone: [nil]) == nil' '(`@object detect: [:`each | | `@temps| `@.Statements] ifNone: [nil]) ~= nil' '(`@object detect: [:`each | | `@temps| `@.Statements] ifNone: [nil]) ~~ nil' '`@object detect: [:`each | | `@temps| `@.Statements] ifNone: [| `@temps1 | `@.Statements2. ^`@anything]') name: (#anyAllSatisfyNotUsed << #browser >> 'Uses detect:ifNone: instead of any/allSatisfy:') asString
assignmentInIfTrue ^self createParseTreeRule: #('`@boolean ifTrue: [| `@temps1 | `@.Statements1. `var := `@object1] ifFalse: [| `@temps2 | `@.Statements2. `var := `@object2]' '`@boolean ifFalse: [| `@temps1 | `@.Statements1. `var := `@object1] ifTrue: [| `@temps2 | `@.Statements2. `var := `@object2]') name: (#SameAssignmentInConditionals << #browser >> 'Assignment to same variable at end of ifTrue:ifFalse: blocks') asString
atIfAbsent ^self createParseTreeRule: #('`@object at: `@atArg ifAbsent: [| `@temps | `@.Statements. `@object at: `@atArg put: `@putArg]' '`@object at: `@atArg ifAbsent: [| `@temps | `@.Statements. `@object at: `@atArg put: `@putArg. `@.xStatements1. `@putArg]') name: (#UsesAtIfAbsent << #browser >> 'Uses at:ifAbsent: instead of at:ifAbsentPut:') asString
collectionProtocol ^self createParseTreeRule: #('`@collection do: [:`each | | `@temps | `@.Statements1. `@object add: `@arg. `@.Statements2]' '`@collection do: [:`each | | `@temps | `@.Statements1. `@condition ifTrue: [| `@blockTemps | `@.BlockStatements1. `@object add: `each. `@.BlockStatements2]. `@.Statements2]' '`@collection do: [:`each | | `@temps | `@.Statements1. `@condition ifFalse: [| `@blockTemps | `@.BlockStatements1. `@object add: `each. `@.BlockStatements2]. `@.Statements2]') name: (#CollectionProtocolNotUsed << #browser >> 'Uses do: instead of collect: or select:''s') asString
collectSelectNotUsed ^self createParseTreeRule: #('`{:node | node isMessage and: [(#(#select: #collect: #reject:) includes: node selector) and: [node isUsed not and: [node arguments first isBlock]]]}') name: (#CollectSelectResultIgnored << #browser >> 'Doesn''t use the result of a collect:/select:') asString
consistencyCheck ^self createParseTreeRule: #('`@object size == 0' '`@object size = 0' '`@object size > 0' '`@object size >= 1' '`@object == nil' '`@object = nil' '`@collection at: 1' '`@collection at: `@collection size') name: (#StandardProtocolNotUsed << #browser >> 'Uses "size = 0", "= nil", or "at: 1" instead of "isEmpty", "isNil", or "first"') asString
detectAndAnySatisfy ^self createParseTreeRule: #('`@collection do: [:`each | | `@temps | `@.Statements1. `@condition ifFalse: [| `@BlockTemps | `@.BlockStatements1. ^`each]. `@.Statements2]' '`@collection do: [:`each | | `@temps | `@.Statements1. `@condition ifTrue: [| `@BlockTemps | `@.BlockStatements1. ^`each]. `@.Statements2]' '`@collection do: [:`each | | `@temps | `@.Statements1. `@condition ifFalse: [| `@BlockTemps | `@.BlockStatements1. ^true]. `@.Statements2]' '`@Collection do: [:`each | | `@temps | `@.Statements1. `@condition ifTrue: [| `@BlockTemps | `@.BlockStatements1. ^true]. `@.Statements2]' '`@collection do: [:`each | | `@temps | `@.Statements1. `@condition ifFalse: [| `@BlockTemps | `@.BlockStatements1. ^false]. `@.Statements2]' '`@collection do: [:`each | | `@temps | `@.Statements1. `@condition ifTrue: [| `@BlockTemps | `@.BlockStatements1. ^false]. `@.Statements2]') name: (#DoUsedInsteadOfAnyAllSatisfy << #browser >> 'Uses do: instead of any/allSatisfy: or detect:''s') asString
guardingClause | detector matcher | detector := self new. detector name: (#GuardingClauses << #browser >> 'Guarding clauses') asString. matcher := ParseTreeSearcher new. matcher matchesAnyMethodOf: #('`@MethodName: `@args | `@temps | `@.Statements. `@condition ifTrue: [| `@BlockTemps | `.Statement1. `.Statement2. `@.BStatements]' '`@MethodName: `@args | `@temps | `@.Statements. `@condition ifFalse: [| `@BlockTemps | `.Statement1. `.Statement2. `@.BStatements]') do: [:aNode :answer | answer isNil ifTrue: [aNode body statements last] ifFalse: [answer]]. detector matcher: matcher. ^detector
ifTrueReturns | detector matcher | detector := self new. detector name: (#ConditionalReturns << #browser >> 'ifTrue:/ifFalse: returns instead of and:/or:''s') asString. matcher := ParseTreeSearcher new. matcher matchesAnyOf: #('| `@temps | ``@.Statements. ``@object ifTrue: [^``@value1]. ^``@value2' '| `@temps | ``@.Statements. ``@object ifFalse: [^``@value1]. ^``@value2') do: [:aNode :answer | answer isNil ifTrue: [| node | node := (aNode statements at: aNode statements size - 1) arguments first body statements last value. "``@value1" ((node isLiteralNode and: [#(true false) includes: node value]) or: [node := aNode statements last value. node isLiteralNode and: [#(true false) includes: node value]]) ifTrue: [aNode] ifFalse: [nil]] ifFalse: [answer]]. detector matcher: matcher. ^detector
indexOf ^self createParseTreeRule: #('(`@collection indexOf: `@object) = 0' '(`@collection indexOf: `@object) == 0' '(`@collection indexOf: `@object) ~= 0' '(`@collection indexOf: `@object) ~~ 0') name: (#IndexOfInsteadOfIncludes << #browser >> 'Uses indexOf: instead of includes:') asString
literalArrayCharacters | detector matcher | detector := self new. detector name: (#LiteralArrayWithOnlyChars << #browser >> 'Literal array contains only characters') asString. matcher := ParseTreeSearcher new. matcher matches: '`#literal' do: [:aNode :answer | answer isNil ifTrue: [(aNode value class == Array and: [self isArrayOfCharacters: aNode value]) ifTrue: [aNode] ifFalse: [nil]] ifFalse: [answer]]. detector matcher: matcher. ^detector
minMax | detector matcher | detector := self new. detector name: (#DoesNotUseMinMax << #browser >> 'Uses ifTrue:/ifFalse: instead of min: or max:') asString. matcher := ParseTreeSearcher new. matcher matchesAnyOf: #('(`x `message: `@y) `ifTrue: [`x := `@y]' '(`@x `message: `@y) `ifTrue: [`@x] `ifFalse: [`@y]' '(`@x `message: `@y) `ifTrue: [`v := `@x] `ifFalse: [`v := `@y]') do: [:aNode :answer | answer isNil ifTrue: [((#(#ifTrue: #ifFalse: #ifTrue:ifFalse: #ifFalse:ifTrue:) includes: aNode selector) and: [#(#< #<= #> #>=) includes: aNode receiver selector]) ifTrue: [aNode] ifFalse: [nil]] ifFalse: [answer]]. detector matcher: matcher. ^detector
searchingLiteral | detector matcher | detector := self new. detector name: (#ORsInsteadOfLiteral << #browser >> 'Uses or''s instead of a searching literal') asString. matcher := ParseTreeSearcher new. matcher matchesAnyOf: #('``@object = `#literal or: [``@expression]' '``@object == `#literal or: [``@expression]' '`#literal = ``@object or: [``@expression]' '`#literal == ``@object or: [``@expression]' '``@expression | (``@object = `#literal)' '``@expression | (``@object == `#literal)' '``@expression | (`#literal = ``@object)' '``@expression | (`#literal == ``@object)') do: [:aNode :answer | answer isNil ifTrue: [(self isSearchingLiteralExpression: aNode) ifTrue: [aNode] ifFalse: [nil]] ifFalse: [answer]]. detector matcher: matcher. ^detector
sizeCheck ^self createParseTreeRule: (#(#do: #collect: #reject: #select:) collect: [:each | '`@object size > 0 ifTrue: [`@object ' , each , ' [:`each | | `@temps | `@.Statements1]. `@.Statements2]']) , (#(#do: #collect: #reject: #select:) collect: [:each | '`@object isEmpty ifFalse: [`@object ' , each , ' [:`each | | `@temps | `@.Statements1]. `@.Statements2]']) name: (#UnnecessarySizeCheck << #browser >> 'Unnecessary size check') asString
toDo | detector matcher | detector := self new. detector name: (#DoesNotUseDoWithDoOrTimesRepeat << #browser >> 'Uses to:do: instead of do:, with:do: or timesRepeat:') asString. matcher := ParseTreeSearcher new. matcher matches: '1 to: ``@object size do: [:`each | | `@temps | `@.Statements]' do: [:aNode :answer | answer isNil ifTrue: [| varName variableMatcher | varName := aNode arguments last arguments first. "`each" variableMatcher := ParseTreeSearcher new. variableMatcher matchesTree: varName do: [:node :ans | ans and: [node parent isMessage and: [node parent selector == #at:]]]. (variableMatcher executeTree: aNode arguments last body initialAnswer: true) ifTrue: [aNode] ifFalse: [nil]] ifFalse: [answer]]. detector matcher: matcher. ^detector
toDoCollect ^self createParseTreeRule: #( '| `@temps1 | `@.Stmts1. `collection := Array new: `@size. `@.Stmts2. 1 to: `@size do: [:`i | | `@Btemps2 | `@.BStmts1. `collection at: `i put: `@obj. `@.BStmt2]. `@.Stmts3' "-------------" '| `@temps1 | `@.Stmts1. `collection := Array new: `@size. `@.Stmts2. 1 to: `collection size do: [:`i | | `@Btemps2 | `@.BStmts1. `collection at: `i put: `@obj. `@.BStmt2]. `@.Stmts3') name: (#todoDoesntUseCollect << #browser >> 'to:do: doesn''t use collect:') asString
toDoWithIncrement ^self createParseTreeRule: #( '`@i to: `@j do: [:`e | | `@temps | `@.Stmts. `x := `x + 1. `@.Stmts2]' '`@i to: `@j by: `@k do: [:`e | | `@temps | `@.Stmts. `x := `x + `@k. `@.Stmts2]') name: (#toDoLoopIncrementsCounter << #browser >> 'to:do: loop also increments a counter') asString
whileTrue ^self createParseTreeRule: #('| `@temps | `@.Statements1. [`index <= `@stop] whileTrue: [| `@blockTemps | `@.BlockStmts1. `index := `index + 1]. `@.Statements2' '| `@temps | `@.Statements1. [`index < `@stop] whileTrue: [| `@blockTemps | `@.BlockStmts1. `index := `index + 1]. `@.Statements2' '| `@temps | `@.Statements1. [`index >= `@stop] whileTrue: [| `@blockTemps | `@.BlockStmts1. `index := `index - 1]. `@.Statements2' '| `@temps | `@.Statements1. [`index > `@stop] whileTrue: [| `@blockTemps | `@.BlockStmts1. `index := `index - 1]. `@.Statements2') name: (#whileTrueInsteadOfToDo << #browser >> 'Uses whileTrue: instead of to:do:') asString
</methods> <methods> Refactory.Browser.ParseTreeLintRule class <category>bugs</category>
booleanPrecedence ^self createParseTreeRule: #('`@object1 | `@object2 = `@object3' '`@object1 | `@object2 == `@object3' '`@object1 & `@object2 = `@object3' '`@object1 & `@object2 == `@object3' '`@object1 | `@object2 ~= `@object3' '`@object1 | `@object2 ~~ `@object3' '`@object1 & `@object2 ~= `@object3' '`@object1 & `@object2 ~~ `@object3') name: (#PossiblyIncorrectBooleanPrecedence << #browser >> 'Uses A | B = C instead of A | (B = C)') asString
</methods> <methods> Refactory.Browser.ParseTreeLintRule class <category>private</category>
createMatcherFor: codeStrings method: aBoolean | matcher | matcher := ParseTreeSearcher new. aBoolean ifTrue: [matcher matchesAnyMethodOf: codeStrings do: [:aNode :answer | aNode]] ifFalse: [matcher matchesAnyOf: codeStrings do: [:aNode :answer | aNode]]. ^matcher
isArrayOfCharacters: anArray anArray isEmpty ifTrue: [^false]. 1 to: anArray size do: [:each | (anArray at: each) class == Character ifFalse: [^false]]. ^true
isSearchingLiteralExpression: aMessageNode | equalNode expressionNode | equalNode := aMessageNode selector = #| ifTrue: [aMessageNode arguments first] ifFalse: [aMessageNode receiver]. expressionNode := equalNode receiver isLiteralNode ifTrue: [equalNode arguments first] ifFalse: [equalNode receiver]. ^self isSearchingLiteralExpression: aMessageNode for: expressionNode
isSearchingLiteralExpression: aSearchingNode for: anObjectNode | argument arguments | aSearchingNode isMessage ifFalse: [^false]. arguments := aSearchingNode arguments. arguments size = 1 ifFalse: [^false]. argument := arguments first. (#(#= #==) includes: aSearchingNode selector) ifTrue: [^(aSearchingNode receiver = anObjectNode and: [aSearchingNode arguments first isLiteralNode]) or: [aSearchingNode arguments first = anObjectNode and: [aSearchingNode receiver isLiteralNode]]]. aSearchingNode selector = #| ifTrue: [^(self isSearchingLiteralExpression: aSearchingNode receiver for: anObjectNode) and: [self isSearchingLiteralExpression: argument for: anObjectNode]]. aSearchingNode selector = #or: ifFalse: [^false]. argument isBlock ifFalse: [^false]. argument body statements size = 1 ifFalse: [^false]. ^(self isSearchingLiteralExpression: aSearchingNode receiver for: anObjectNode) and: [self isSearchingLiteralExpression: argument body statements first for: anObjectNode]
modifiesTree: aCollectionTree in: aParseTree | notifier args | notifier := ParseTreeSearcher new. args := Array with: (RBPatternVariableNode named: '`@object'). notifier matchesAnyTreeOf: (#(#add: #addAll: #remove: #removeAll:) collect: [:each | RBMessageNode receiver: aCollectionTree selector: each arguments: args]) do: [:aNode :answer | true]. ^notifier executeTree: aParseTree initialAnswer: false
</methods> <methods> Refactory.Browser.ParseTreeLintRule class <category>instance creation</category>
createParseTreeRule: codeStrings method: aBoolean name: aName ^(self new) name: aName; matcher: (self createMatcherFor: codeStrings method: aBoolean); yourself
createParseTreeRule: codeStrings name: aName ^self createParseTreeRule: codeStrings method: false name: aName
</methods> <methods> Refactory.Browser.ParseTreeLintRule class <category>unnecessary code</category>
asOrderedCollectionNotNeeded ^self createParseTreeRule: #('`@node addAll: `{:node | node isMessage and: [#(asOrderedCollection asArray) includes: node selector]}') name: (#asArrayNotNeeded << #browser >> '#asOrderedCollection/#asArray not needed') asString
assignmentInBlock ^self createParseTreeRule: #( '`@cursor showWhile: [| `@temps | `@.Statements1. `var := `@object]' '`@cursor showWhile: [| `@temps | `@.Statements1. ^`@object]' '[| `@temps | `@.Statements. `var := `@object] valueNowOrOnUnwindDo: `@block' '[| `@temps | `@.Statements. ^`@object] valueNowOrOnUnwindDo: `@block' '[| `@temps | `@.Statements. `var := `@object] valueOnUnwindDo: `@block' '[| `@temps | `@.Statements. ^`@object] valueOnUnwindDo: `@block' '[| `@temps | `@.Statements. `var := `@object] ensure: `@block' '[| `@temps | `@.Statements. ^`@object] ensure: `@block' '[| `@temps | `@.Statements. `var := `@object] ifCurtailed: `@block' '[| `@temps | `@.Statements. ^`@object] ifCurtailed: `@block' ) name: (#UnnecessaryAssignmentInBlock << #browser >> 'Unnecessary assignment or return in block') asString
endTrueFalse | detector matcher | detector := self new. detector name: (#CheckForSameAtEndOfConditionals << #browser >> 'Check for same statements at end of ifTrue:ifFalse: blocks') asString. matcher := (ParseTreeSearcher new) matchesAnyOf: #('`@object ifTrue: [| `@temps1 | `@.Statements1. `.Statement] ifFalse: [| `@temps2 | `@.Statements2. `.Statement]' '`@object ifTrue: [| `@temps1 | `.Statement. `@.Statements1] ifFalse: [| `@temps2 | `.Statement. `@.Statements2]' '`@object ifFalse: [| `@temps1 | `@.Statements1. `.Statement] ifTrue: [| `@temps2 | `@.Statements2. `.Statement]' '`@object ifFalse: [| `@temps1 | `.Statement. `@.Statements1] ifTrue: [| `@temps2 | `.Statement. `@.Statement2]') do: [:aNode :answer | answer isNil ifTrue: [| node | node := aNode arguments first body statements last. (node isVariable and: [node = aNode arguments last body statements last]) ifTrue: [nil] ifFalse: [aNode]] ifFalse: [answer]]; yourself. detector matcher: matcher. ^detector
equalsTrue | detector matcher | detector := self new. detector name: (#UnnecessaryEqTrue << #browser >> 'Unnecessary "= true"') asString. matcher := (ParseTreeSearcher new) matchesAnyOf: #('true' 'false') do: [:aNode :answer | answer isNil ifTrue: [(aNode parent isMessage and: [#(#= #== #~= #~~) includes: aNode parent selector]) ifTrue: [aNode] ifFalse: [nil]] ifFalse: [answer]]; yourself. detector matcher: matcher. ^detector
extraBlock ^self createParseTreeRule: #('`{:node | node isMessage and: [node receiver isBlock and: [node parent isCascade not and: [#(#value #value: #value:value: #value:value:value: #valueWithArguments) includes: node selector]]]}') name: (#BlockImmediatelyEvaluated << #browser >> 'Block immediately evaluated') asString
unnecessaryAssignment ^self createParseTreeRule: #('^`{:aNode | aNode isAssignment and: [(aNode whoDefines: aNode variable name) notNil]}') name: (#UnnecessaryTempAssignment << #browser >> 'Unnecessary assignment to a temporary variable') asString
</methods> <methods> Refactory.Browser.ParseTreeLintRule class <category>possible bugs</category>
equalNotUsed ^self createParseTreeRule: #('`{:node | node isMessage and: [node isUsed not and: [#(#= #== #~= #~~ #< #> #<= #>=) includes: node selector]]}') name: (#EqualResultNotUsed << #browser >> 'Doesn''t use the result of a =, ~=, etc.') asString
fileBlocks ^self createParseTreeRule: #('[| `@temps | `var := `@object. `@.statements] valueNowOrOnUnwindDo: [`var `@messages: `@args]' '[| `@temps | `var := `@object. `@.statements] valueOnUnwindDo: [`var `@messages: `@args]' '[| `@temps | `var := `@object. `@.statements] ensure: [`var `@messages: `@args]' '[| `@temps | `var := `@object. `@.statements] ifCurtailed: [`var `@messages: `@args]') name: (#AssignmentInUnwindBlock << #browser >> 'Assignment inside unwind blocks should be outside.') asString
missingYourself ^self createParseTreeRule: #('`{:node | node isMessage and: [node parent isCascade and: [node isDirectlyUsed and: [node selector ~~ #yourself]]]}') name: (#PossibleMissingYourself << #browser >> 'Possible missing "; yourself"') asString
modifiesCollection | detector matcher | detector := self new. detector name: (#ModifiesWhileIterating << #browser >> 'Modifies collection while iterating over it') asString. matcher := (ParseTreeSearcher new) matchesAnyOf: #('`@object do: [:`each | | `@temps | ``@.Statements]' '`@object collect: [:`each | | `@temps | ``@.Statements]' '`@object select: [:`each | | `@temps | ``@.Statements]' '`@object reject: [:`each | | `@temps | ``@.Statements]' '`@object inject: `@value into: [:`sum :`each | | `@temps | ``@.Statements]') do: [:aNode :answer | answer isNil ifTrue: [(self modifiesTree: aNode receiver in: aNode arguments last) ifTrue: [aNode] ifFalse: [nil]] ifFalse: [answer]]; yourself. detector matcher: matcher. ^detector
returnInEnsure | detector matcher returnMatcher | detector := self new. detector name: (#ReturnInEnsureColon << #browser >> 'Contains a return in an ensure: block') asString. returnMatcher := ParseTreeSearcher new. returnMatcher matches: '^`@object' do: [:aNode :answer | true]. matcher := ParseTreeSearcher new. matcher matchesAnyOf: #('``@rcv ensure: [| `@temps | ``@.Stmts]' '``@rcv valueNowOrOnUnwindDo: [| `@temps | ``@.Stmts]' '``@rcv ifCurtailed: [| `@temps | ``@.Stmts]' '``@rcv valueOnUnwindDo: [| `@temps | ``@.Stmts]') do: [:aNode :answer | answer isNil ifTrue: [(returnMatcher executeTree: aNode arguments first initialAnswer: false) ifTrue: [aNode] ifFalse: [nil]] ifFalse: [answer]]. detector matcher: matcher. ^detector
returnsIfTrue ^self createParseTreeRule: #('^`@condition ifTrue: [| `@temps | `@.statements]' '^`@condition ifFalse: [| `@temps | `@.statements]') name: (#ReturnsValueOfAsymmetricConditional << #browser >> 'Returns value of ifTrue:/ifFalse: without ifFalse:/ifTrue: block') asString
threeElementPoint | detector matcher | detector := self new. detector name: (#PossibleThreeElementPoint << #browser >> 'Possible three element point (e.g., x @ y + q @ r)') asString. matcher := (ParseTreeSearcher new) matches: '``@x @ ``@y' do: [:aNode :answer | answer isNil ifTrue: [| current | current := aNode parent. [current isNil or: [current isMessage and: [current selector == #@ or: [current selector isInfix not]]]] whileFalse: [current := current parent]. (current isNil or: [current isMessage and: [current selector isInfix not]]) ifTrue: [nil] ifFalse: [aNode]] ifFalse: [answer]]; yourself. detector matcher: matcher. ^detector
usesAdd ^self createParseTreeRule: #('`{:node | node isMessage and: [(node selector == #add: or: [node selector == #addAll:]) and: [node isDirectlyUsed]]}') name: (#UsesResultOfAdd << #browser >> 'Uses the result of an add: message') asString
</methods> <methods> Refactory.Browser.ParseTreeLintRule class <category>deprecated</category>
ifTrueBlocks ^self createParseTreeRule: #( '`@condition ifTrue: `{:node | node isBlock not} ifFalse: `@block' '`@condition ifTrue: `@block ifFalse: `{:node | node isBlock not}' '`@condition ifFalse: `{:node | node isBlock not} ifTrue: `@block' '`@condition ifFalse: `@block ifTrue: `{:node | node isBlock not}' '`@condition ifTrue: `{:node | node isBlock not}' '`@condition ifFalse: `{:node | node isBlock not}') name: (#NonBlockConditionals << #browser >> 'Non-blocks in ifTrue:/ifFalse: messages') asString
precedence ^self createParseTreeRule: #('`{:node | node isMessage and: [node hasParentheses not and: [#(#+ #-) includes: node selector]]} * `@C') name: (#PossiblyWrongPrecedence << #browser >> 'Inspect instances of "A + B * C" might be "A + (B * C)"') asString
stringConcatenation | detector matcher concatenationMatcher | detector := self new. detector name: (#ConcatenationInsteadOfStreams << #browser >> 'String concatenation instead of streams') asString. matcher := ParseTreeSearcher new. concatenationMatcher := ParseTreeSearcher new. concatenationMatcher matches: '`@receiver , `@argument' do: [:aNode :answer | true]. matcher matchesAnyOf: #('``@collection do: [:`each | | `@temps | ``@.Statements]' '``@collection do: [:`each | | `@temps | ``@.Statements] separatedBy: [| `@temps1 | ``@.Statements1]' '``@number to: ``@endNumber do: [:`i | | `@temps | ``@.Statements]' '``@collection detect: [:`each | | `@temps | ``@.Statements]' '``@collection detect: [:`each | | `@temps | ``@.Statements] ifNone: [| `@temps1 | ``@.Statements1]' '``@collection select: [:`each | | `@temps | ``@.Statements]' '``@collection inject: ``@value into: [:`each | | `@temps | ``@.Statements]') do: [:aNode :answer | answer isNil ifTrue: [(aNode arguments anySatisfy: [:each | each isBlock and: [concatenationMatcher executeTree: each initialAnswer: false]]) ifTrue: [aNode] ifFalse: [nil]] ifFalse: [answer]]. detector matcher: matcher. ^detector
unoptimizedAndOr ^self createParseTreeRule: #('(`@a and: `@b) and: `@c' '(`@a or: `@b) or: `@c') name: (#UnoptimizedAndOr << #browser >> 'Uses "(a and: [b]) and: [c]" instead of "a and: [b and: [c]]"') asString
unoptimizedToDo ^self createParseTreeRule: #('(`@a to: `@b) do: `@c') name: (#UnoptimizedToDo << #browser >> 'Uses (to:)do: instead of to:do:') asString
yourselfNotUsed ^self createParseTreeRule: #('`{:node | node parent isUsed not} yourself') name: (#YourselfNotUsed << #browser >> 'Doesn''t use the result of a yourself message') asString
</methods> <methods> Refactory.Browser.MultiEnvironment <category>adding</category>
addClass: aClass into: aValue (environmentDictionaries at: aValue ifAbsentPut: [SelectorEnvironment new]) addClass: aClass
addClass: aClass selector: aSymbol into: aValue (environmentDictionaries at: aValue ifAbsentPut: [SelectorEnvironment new]) addClass: aClass selector: aSymbol
</methods> <methods> Refactory.Browser.MultiEnvironment <category>accessing</category>
environmentNamed: aString ^environmentDictionaries at: aString ifAbsent: [SelectorEnvironment new]
environments ^environmentDictionaries keys
problemCount ^environmentDictionaries size
selectEnvironment: aValue environment := environmentDictionaries at: aValue ifAbsent: [SelectorEnvironment new]
</methods> <methods> Refactory.Browser.MultiEnvironment <category>initialize-release</category>
initialize super initialize. environmentDictionaries := Dictionary new. environment := SelectorEnvironment new
</methods> <methods> Refactory.Browser.MultiEnvironment <category>testing</category>
isEmpty ^environmentDictionaries isEmpty
</methods> <methods> Refactory.Browser.MultiEnvironment <category>removing</category>
removeClass: aClass into: aValue (environmentDictionaries at: aValue ifAbsent: [SelectorEnvironment new]) removeClass: aClass
removeClass: aClass selector: aSelector into: aValue (environmentDictionaries at: aValue ifAbsentPut: [SelectorEnvironment new]) removeClass: aClass selector: aSelector
</methods> <methods> Refactory.Browser.SmalllintContext <category>private</category>
addLiteralsFor: aCompiledMethod aCompiledMethod withAllBlockMethodsDo: [:each | each literalsDo: [:literal | self checkLiteral: literal]]
buildParseTree | tree | tree := self selectedClass parseTreeFor: self selector. tree isNil ifTrue: [^RBParser parseMethod: 'method']. ^tree
checkLiteral: aLiteral | binding | aLiteral isSymbol ifTrue: [literals add: aLiteral]. aLiteral isVariableBinding ifTrue: [binding := aLiteral nonDeferredBinding. binding isNil ifTrue: [binding := Undeclared bindingFor: aLiteral key]. binding notNil ifTrue: [binding isForClass ifTrue: [literals add: binding value. literals addAll: binding value environment withAllEnvironments]. binding isForNameSpace ifTrue: [literals addAll: binding value withAllEnvironments]. binding isForGeneral ifTrue: [literals add: binding]]]. aLiteral isBindingReference ifTrue: [self checkLiteral: aLiteral bindingOrNil]. aLiteral class == Array ifTrue: [aLiteral do: [:each | self checkLiteral: each]]
computeLiterals literalSemaphore := Semaphore new. literalProcess := [self primitiveComputeLiterals] fork
computeLiteralsForClass: aClass self computeLiteralsForMethodsInClass: aClass; computeLiteralsForMethodsInClass: aClass class; computeLiteralsForNameSpace: aClass asNameSpace
computeLiteralsForMethodsInClass: aClass (selectors addAll: aClass selectors) do: [:sel | self computeLiteralsForSelector: sel in: aClass. Processor yield]
computeLiteralsForNameSpace: aNameSpace aNameSpace bindingsDo: [:each | each isForGeneral ifTrue: [self computeLiteralsForSharedVariable: each]. each isForNameSpace ifTrue: [self computeLiteralsForNameSpace: each value]. each isForClass ifTrue: [self computeLiteralsForClass: each value]]
computeLiteralsForSelector: aSelector in: aClass | method | method := aClass compiledMethodAt: aSelector ifAbsent: [nil]. method isNil ifTrue: [^self]. self addLiteralsFor: method
computeLiteralsForSharedVariable: aBinding aBinding hasInitializer ifTrue: [self addLiteralsFor: aBinding method]
computeMessages | searcher | selfMessages := Set new. superMessages := Set new. messages := Set new. searcher := ParseTreeSearcher new. searcher matches: 'self `@message: ``@args' do: [:aNode :answer | selfMessages add: aNode selector]; matches: 'super `@message: ``@args' do: [:aNode :answer | superMessages add: aNode selector]; matches: '``@receiver `@message: ``@args' do: [:aNode :answer | messages add: aNode selector]. searcher executeTree: self parseTree initialAnswer: nil
primitiveComputeLiterals | semaphore | literals := IdentitySet new: 25000. literals addAll: self specialSelectors. selectors := IdentitySet new. self computeLiteralsForNameSpace: Root. semaphore := literalSemaphore. literalSemaphore := nil. self signalProcesses: semaphore. ^literalProcess := nil
signalProcesses: aSemaphore aSemaphore isNil ifTrue: [^self]. [aSemaphore isEmpty] whileFalse: [aSemaphore signal]
specialSelectors ^(OpcodePool at: #SpecialSelectorDictionary) keys
</methods> <methods> Refactory.Browser.SmalllintContext <category>accessing</category>
compiledMethod ^compiledMethod notNil ifTrue: [compiledMethod] ifFalse: [compiledMethod := class compiledMethodAt: selector]
instVarNames ^self selectedClass allInstVarNames
literals literalSemaphore isNil ifTrue: [literals isNil ifTrue: [self computeLiterals. literalSemaphore wait]] ifFalse: [literalSemaphore wait]. ^literals
messages messages isNil ifTrue: [self computeMessages]. ^messages
methodSource | source | source := self selectedClass sourceCodeAt: self selector. ^source isNil ifTrue: [''] ifFalse: [source]
nameSpace ^nameSpace
parseTree ^parseTree isNil ifTrue: [parseTree := self buildParseTree] ifFalse: [parseTree]
protocol ^self selectedClass whichCategoryIncludesSelector: self selector
protocols ^Array with: self protocol
selectedClass ^class
selectedClass: aBehavior class := aBehavior. nameSpace := class environment. self selector: nil
selector ^selector
selector: anObject selector := anObject. parseTree := compiledMethod := selfMessages := superMessages := messages := nil
selectors literalSemaphore isNil ifTrue: [selectors isNil ifTrue: [self computeLiterals. literalSemaphore wait]] ifFalse: [literalSemaphore wait]. ^selectors
selfMessages selfMessages isNil ifTrue: [self computeMessages]. ^selfMessages
superMessages superMessages isNil ifTrue: [self computeMessages]. ^superMessages
</methods> <methods> Refactory.Browser.SmalllintContext <category>testing</category>
implements: aSelector ^self selectors includes: aSelector
isAbstract: aClass ^(aClass isMeta or: [(self literals includes: aClass name) or: [self literals includes: aClass]]) not
uses: anObject ^self literals includes: anObject
</methods> <methods> Refactory.Browser.SmalllintContext <category>initialize-release</category>
initialize self computeLiterals
release literalProcess notNil ifTrue: [literalProcess terminate]. super release
</methods> <methods> Refactory.Browser.SmalllintContext class <category>instance creation</category>
new ^(super new) initialize; yourself
newNoCache ^self basicNew
</methods> <methods> Refactory.Browser.ParseTreeTransformationRule <category>initialize-release</category>
rewriteUsing: searchReplacer rewriteRule := searchReplacer. self resetResult
</methods> <methods> Refactory.Browser.ParseTreeTransformationRule <category>accessing</category>
checkMethod: aSmalllintContext (rewriteRule canMatchMethod: aSmalllintContext compiledMethod) ifFalse: [^self]. class := aSmalllintContext selectedClass. (rewriteRule executeTree: aSmalllintContext parseTree) ifTrue: [(self class recursiveSelfRule executeTree: rewriteRule tree initialAnswer: false) ifFalse: [changes compile: rewriteRule tree newSource in: class classified: aSmalllintContext protocol]]
</methods> <methods> Refactory.Browser.ParseTreeTransformationRule <category>rules</category>
superSends | rule | rule := ParseTreeRewriter new. rule replace: 'super' with: 'self' when: [:aNode | | message | message := aNode parent. message notNil and: [message isMessage and: [(class withAllSubclasses anySatisfy: [:each | each includesSelector: message selector]) not]]]. self rewriteUsing: rule
</methods> <methods> Refactory.Browser.ParseTreeTransformationRule class <category>transformations</category>
assignmentInIfTrue ^self rewrite: #( #('``@Boolean ifTrue: [`variable := ``@true] ifFalse: [`variable := ``@false]' "->" '`variable := ``@Boolean ifTrue: [``@true] ifFalse: [``@false]') #('``@Boolean ifFalse: [`variable := ``@true] ifTrue: [`variable := ``@false]' "->" '`variable := ``@Boolean ifFalse: [``@true] ifTrue: [``@false]')) methods: false name: (#MoveAssignmentOutsideOfConditionals << #browser >> 'Move variable assignment outside of single statement ifTrue:ifFalse: blocks') asString
atIfAbsent ^self rewrite: #( #('``@dictionary at: ``@key ifAbsent: [| `@temps | ``@.Statements1. ``@dictionary at: ``@key put: ``@object. ``@.Statements2. ``@object]' "->" '``@dictionary at: ``@key ifAbsentPut: [| `@temps | ``@.Statements1. ``@.Statements2. ``@object]') #('``@dictionary at: ``@key ifAbsent: [| `@temps | ``@.Statements. ``@dictionary at: ``@key put: ``@object]' "->" '``@dictionary at: ``@key ifAbsentPut: [| `@temps | ``@.Statements. ``@object]')) methods: false name: (#ChangeIfAbsentToPut << #browser >> 'at:ifAbsent: -> at:ifAbsentPut:') asString
betweenAnd ^self rewrite: #( #('``@a >= ``@b and: [``@a <= ``@c]' "->" '``@a between: ``@b and: ``@c') #('``@a >= ``@b & (``@a <= ``@c)' "->" '``@a between: ``@b and: ``@c') #('``@b <= ``@a and: [``@a <= ``@c]' "->" '``@a between: ``@b and: ``@c') #('``@b <= ``@a & (``@a <= ``@c)' "->" '``@a between: ``@b and: ``@c') #('``@a <= ``@c and: [``@a >= ``@b]' "->" '``@a between: ``@b and: ``@c') #('``@a <= ``@c & (``@a >= ``@b)' "->" '``@a between: ``@b and: ``@c') #('``@c >= ``@a and: [``@a >= ``@b]' "->" '``@a between: ``@b and: ``@c') #('``@c >= ``@a & (``@a >= ``@b)' "->" '``@a between: ``@b and: ``@c') #('``@a >= ``@b and: [``@c >= ``@a]' "->" '``@a between: ``@b and: ``@c') #('``@a >= ``@b & (``@c >= ``@a)' "->" '``@a between: ``@b and: ``@c') #('``@b <= ``@a and: [``@c >= ``@a]' "->" '``@a between: ``@b and: ``@c') #('``@b <= ``@a & (``@c >= ``@a)' "->" '``@a between: ``@b and: ``@c') #('``@a <= ``@c and: [``@b <= ``@a]' "->" '``@a between: ``@b and: ``@c') #('``@a <= ``@c & (``@b <= ``@a)' "->" '``@a between: ``@b and: ``@c') #('``@c >= ``@a and: [``@b <= ``@a]' "->" '``@a between: ``@b and: ``@c') #('``@c >= ``@a & (``@b <= ``@a)' "->" '``@a between: ``@b and: ``@c')) methods: false name: (#UseBetweenAnd << #browser >> '"a >= b and: [a <= c]" -> "a between: b and: c"') asString
cascadedNextPutAlls ^self rewrite: #( #('``@rcvr nextPutAll: ``@object1 , ``@object2' "->" '``@rcvr nextPutAll: ``@object1; nextPutAll: ``@object2') #('``@rcvr show: ``@object1 , ``@object2' "->" '``@rcvr show: ``@object1; show: ``@object2')) methods: false name: (#UseCascadedNextPutsAlls << #browser >> 'Use cascaded nextPutAll:''s instead of #, in #nextPutAll:') asString
detectIfNone ^self rewrite: #( #('(``@collection detect: [:`each | | `@temps | ``@.Statements] ifNone: [nil]) isNil' "->" '(``@collection anySatisfy: [:`each | | `@temps | ``@.Statements]) not') #('(``@collection detect: [:`each | | `@temps | ``@.Statements] ifNone: [nil]) = nil' "->" '(``@collection anySatisfy: [:`each | | `@temps | ``@.Statements]) not') #('(``@collection detect: [:`each | | `@temps | ``@.Statements] ifNone: [nil]) == nil' "->" '(``@collection anySatisfy: [:`each | | `@temps | ``@.Statements]) not') #('(``@collection detect: [:`each | | `@temps | ``@.Statements] ifNone: [nil]) notNil' "->" '``@collection anySatisfy: [:`each | | `@temps | ``@.Statements]') #('(``@collection detect: [:`each | | `@temps | ``@.Statements] ifNone: [nil]) ~= nil' "->" '``@collection anySatisfy: [:`each | | `@temps | ``@.Statements]') #('(``@collection detect: [:`each | | `@temps | ``@.Statements] ifNone: [nil]) ~~ nil' "->" '``@collection anySatisfy: [:`each | | `@temps | ``@.Statements]')) methods: false name: (#detectifNoneAnySatisfy << #browser >> 'detect:ifNone: -> anySatisfy:') asString
equalNil ^self rewrite: #( #('``@object = nil' "->" '``@object isNil') #('``@object == nil' "->" '``@object isNil') #('``@object ~= nil' "->" '``@object notNil') #('``@object ~~ nil' "->" '``@object notNil')) methods: false name: (#UseIsNilOrNotNil << #browser >> '= nil -> isNil AND ~= nil -> notNil') asString
guardClause ^self rewrite: #( #('`@methodName: `@args | `@temps | `@.Statements. `@condition ifTrue: [| `@trueTemps | `.Statement1. `.Statement2. `@.Statements1]' "->" '`@methodName: `@args | `@temps `@trueTemps | `@.Statements. `@condition ifFalse: [^self]. `.Statement1. `.Statement2. `@.Statements1') #('`@methodName: `@args | `@temps | `@.Statements. `@condition ifFalse: [| `@falseTemps | `.Statement1. `.Statement2. `@.Statements1]' "->" '`@methodName: `@args | `@temps `@falseTemps | `@.Statements. `@condition ifTrue: [^self]. `.Statement1. `.Statement2. `@.Statements1')) methods: true name: (#EliminateGuardingClauses << #browser >> 'Eliminate guarding clauses') asString
minMax ^self rewrite: #( #('``@a < ``@b ifTrue: [``@a] ifFalse: [``@b]' "->" '``@a min: ``@b') #('``@a <= ``@b ifTrue: [``@a] ifFalse: [``@b]' "->" '``@a min: ``@b') #('``@a > ``@b ifTrue: [``@a] ifFalse: [``@b]' "->" '``@a max: ``@b') #('``@a >= ``@b ifTrue: [``@a] ifFalse: [``@b]' "->" '``@a max: ``@b') #('``@a < ``@b ifTrue: [``@b] ifFalse: [``@a]' "->" '``@a max: ``@b') #('``@a <= ``@b ifTrue: [``@b] ifFalse: [``@a]' "->" '``@a max: ``@b') #('``@a > ``@b ifTrue: [``@b] ifFalse: [``@a]' "->" '``@a min: ``@b') #('``@a >= ``@b ifTrue: [``@b] ifFalse: [``@a]' "->" '``@a min: ``@b') #('`a < ``@b ifTrue: [`a := ``@b]' "->" '`a := `a max: ``@b') #('`a <= ``@b ifTrue: [`a := ``@b]' "->" '`a := `a max: ``@b') #('`a < ``@b ifFalse: [`a := ``@b]' "->" '`a := `a min: ``@b') #('`a <= ``@b ifFalse: [`a := ``@b]' "->" '`a := `a min: ``@b') #('`a > ``@b ifTrue: [`a := ``@b]' "->" '`a := `a min: ``@b') #('`a >= ``@b ifTrue: [`a := ``@b]' "->" '`a := `a min: ``@b') #('`a > ``@b ifFalse: [`a := ``@b]' "->" '`a := `a max: ``@b') #('`a >= ``@b ifFalse: [`a := ``@b]' "->" '`a := `a max: ``@b') #('``@b < `a ifTrue: [`a := ``@b]' "->" '`a := `a min: ``@b') #('``@b <= `a ifTrue: [`a := ``@b]' "->" '`a := `a min: ``@b') #('``@b < `a ifFalse: [`a := ``@b]' "->" '`a := `a max: ``@b') #('``@b <= `a ifFalse: [`a := ``@b]' "->" '`a := `a max: ``@b') #('``@b > `a ifTrue: [`a := ``@b]' "->" '`a := `a max: ``@b') #('``@b >= `a ifTrue: [`a := ``@b]' "->" '`a := `a max: ``@b') #('``@b > `a ifFalse: [`a := ``@b]' "->" '`a := `a min: ``@b') #('``@b >= `a ifFalse: [`a := ``@b]' "->" '`a := `a min: ``@b')) methods: false name: (#RewriteUsingMinMax << #browser >> 'Rewrite ifTrue:ifFalse: using min:/max:') asString
notElimination ^self rewrite: #( #('``@object not not' "->" '``@object') #('``@object not ifTrue: ``@block' "->" '``@object ifFalse: ``@block') #('``@object not ifFalse: ``@block' "->" '``@object ifTrue: ``@block') #('``@collection select: [:`each | | `@temps | ``@.Statements. ``@object not]' "->" '``@collection reject: [:`each | | `@temps | ``@.Statements. ``@object]') #('``@collection reject: [:`each | | `@temps | ``@.Statements. ``@object not]' "->" '``@collection select: [:`each | | `@temps | ``@.Statements. ``@object]') #('[| `@temps | ``@.Statements. ``@object not] whileTrue: ``@block' "->" '[| `@temps | ``@.Statements. ``@object] whileFalse: ``@block') #('[| `@temps | ``@.Statements. ``@object not] whileFalse: ``@block' "->" '[| `@temps | ``@.Statements. ``@object] whileTrue: ``@block') #('[| `@temps | ``@.Statements. ``@object not] whileTrue' "->" '[| `@temps | ``@.Statements. ``@object] whileFalse') #('[| `@temps | ``@.Statements. ``@object not] whileFalse' "->" '[| `@temps | ``@.Statements. ``@object] whileTrue') #('(``@a <= ``@b) not' "->" '``@a > ``@b') #('(``@a < ``@b) not' "->" '``@a >= ``@b') #('(``@a = ``@b) not' "->" '``@a ~= ``@b') #('(``@a == ``@b) not' "->" '``@a ~~ ``@b') #('(``@a ~= ``@b) not' "->" '``@a = ``@b') #('(``@a ~~ ``@b) not' "->" '``@a == ``@b') #('(``@a >= ``@b) not' "->" '``@a < ``@b') #('(``@a > ``@b) not' "->" '``@a <= ``@b')) methods: false name: (#EliminateUnnecessaryNots << #browser >> 'Eliminate unnecessary not''s') asString
showWhileBlocks ^self rewrite: #( #('``@cursor showWhile: [| `@temps | ``@.Statements. `var := ``@object]' "->" '`var := ``@cursor showWhile: [| `@temps | ``@.Statements. ``@object]') #('``@cursor showWhile: [| `@temps | ``@.Statements. ^``@object]' "->" '^``@cursor showWhile: [| `@temps | ``@.Statements. ``@object]')) methods: false name: (#MoveAssignmentOutOfShowWhile << #browser >> 'Move assignment out of showWhile: blocks') asString
streamLiterals ^self rewrite: #( #('`#l `{:node | node parent isMessage and: [node parent selector = #writeStream or: [node parent selector = #on: and: [node parent receiver = (RBVariableNode named: ''WriteStream'') or: [node parent receiver = (RBVariableNode named: ''ReadWriteStream'')]]]]}' "->" '`{`#l value isEmpty ifTrue: [RBMessageNode receiver: (RBVariableNode named: (`#l value = '''' ifTrue: [''String''] ifFalse: [`#l value class name asString])) selector: #new] ifFalse: [RBMessageNode receiver: `#l copy selector: #copy]}')) methods: false name: (#DoNotUseWriteStreamsOnLiterals << #browser >> 'Convert write streams on literals to be write streams on non-literals') asString
superSends ^(self new) name: (#RewriteSuperMessagesToSelf << #browser >> 'Rewrite super messages to self messages when both refer to same method') asString; superSends; yourself
unwindBlocks ^self rewrite: #( #('[| `@temps | ``@.Statements. `var := ``@object] valueNowOrOnUnwindDo: ``@block' "->" '`var := [| `@temps | ``@.Statements. ``@object] valueNowOrOnUnwindDo: ``@block') #('[| `@temps | ``@.Statements. ^``@object] valueNowOrOnUnwindDo: ``@block' "->" '^[| `@temps | ``@.Statements. ``@object] valueNowOrOnUnwindDo: ``@block') #('[| `@temps | ``@.Statements. `var := ``@object] valueOnUnwindDo: ``@block' "->" '`var := [| `@temps | ``@.Statements. ``@object] valueOnUnwindDo: ``@block') #('[| `@temps | ``@.Statements. ^``@object] valueOnUnwindDo: ``@block' "->" '^[| `@temps | ``@.Statements. ``@object] valueOnUnwindDo: ``@block') #('[| `@temps | ``@.Statements. `var := ``@object] ensure: ``@block' "->" '`var := [| `@temps | ``@.Statements. ``@object] ensure: ``@block') #('[| `@temps | ``@.Statements. ^``@object] ensure: ``@block' "->" '^[| `@temps | ``@.Statements. ``@object] ensure: ``@block') #('[| `@temps | ``@.Statements. `var := ``@object] ifCurtailed: ``@block' "->" '`var := [| `@temps | ``@.Statements. ``@object] ifCurtailed: ``@block') #('[| `@temps | ``@.Statements. ^``@object] ifCurtailed: ``@block' "->" '^[| `@temps | ``@.Statements. ``@object] ifCurtailed: ``@block')) methods: false name: (#MoveAssignmentOutOfEnsure << #browser >> 'Move assignment out of ensure: blocks') asString
</methods> <methods> Refactory.Browser.ParseTreeTransformationRule class <category>accessing</category>
initializeRecursiveSelfRule RecursiveSelfRule := ParseTreeSearcher new. RecursiveSelfRule matchesAnyMethodOf: #('`@methodName: `@args | `@temps | self `@methodName: `@args1' '`@methodName: `@args | `@temps | ^self `@methodName: `@args1') do: [:aNode :answer | true]. ^RecursiveSelfRule
recursiveSelfRule ^RecursiveSelfRule isNil ifTrue: [self initializeRecursiveSelfRule] ifFalse: [RecursiveSelfRule]
</methods> <methods> Refactory.Browser.ParseTreeTransformationRule class <category>instance creation</category>
rewrite: stringArrays methods: aBoolean name: aName | rewriteRule | rewriteRule := ParseTreeRewriter new. stringArrays do: [:each | aBoolean ifTrue: [rewriteRule replaceMethod: each first with: each last] ifFalse: [rewriteRule replace: each first with: each last]]. ^(self new) name: aName; rewriteUsing: rewriteRule; yourself
</methods> <methods> Core.VariableBinding <category>RefactoringBrowser</category>
nonDeferredBinding ^self
</methods> <methods> Refactory.Browser.AndEnvironment <category>accessing</category>
problemCount ^environment isClassEnvironment ifTrue: [self numberClasses] ifFalse: [super problemCount]
</methods> <methods> Kernel.CompiledCode <category>RefactoringBrowser</category>
messagesDo: aBlock self withAllBlockMethodsDo: [:meth | | scanner selector | scanner := InstructionStream on: meth. scanner scanFor: [:byte | selector := scanner peekForSelector. selector == nil ifFalse: [aBlock value: selector]. false]]
</methods> <methods> Refactory.Browser.ClassEnvironment <category>accessing</category>
problemCount ^self numberClasses
</methods> <methods> Kernel.CompiledMethod <category>RefactoringBrowser</category>
equivalentTo: aCompiledMethod ^self = aCompiledMethod or: [self class == aCompiledMethod class and: [self numArgs == aCompiledMethod numArgs and: [| selfParseTree methodParseTree | selfParseTree := mclass decompilerClass new decompile: (mclass defaultSelectorForMethod: self) in: mclass method: self. methodParseTree := aCompiledMethod mclass decompilerClass new decompile: (aCompiledMethod mclass defaultSelectorForMethod: aCompiledMethod) in: aCompiledMethod mclass method: aCompiledMethod. selfParseTree = methodParseTree]]]
</methods> <methods> Refactory.Browser.CompositeRefactoryChange <category>accessing</category>
problemCount ^self changesSize
</methods> <methods> Refactory.Browser.BrowserEnvironment <category>testing</category>
isForSmalllint ^false
</methods> <methods> Refactory.Browser.BrowserEnvironment <category>accessing</category>
problemCount ^self numberSelectors
</methods> <methods> Refactory.Browser.VariableEnvironment <category>accessing</category>
problemCount ^self numberVariables
</methods> <methods> Kernel.DeferredBinding <category>RefactoringBrowser</category>
nonDeferredBinding | binding | binding := method mclass bindingFor: key. binding isNil ifTrue: [binding := method environment bindingFor: key]. ^binding
</methods> "Imported Classes:" self error: 'Attempting to file-in parcel imports. Choose terminate or close' <class> <name>Object</name> <environment>Core</environment> <super></super> <private>false</private> none <imports></imports> <category>Kernel-Objects</category> <attributes> <package>Kernel-Objects</package> </attributes> </class> <class> <name>CompositeRefactoryChange</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.RefactoryChange</super> <private>false</private> none changes <imports></imports> <category>Browser-Change Objects</category> <attributes> <package>Browser-ChangeObjects</package> </attributes> </class> <class> <name>BrowserEnvironment</name> <environment>Refactory.Browser</environment> <super>Core.Object</super> <private>false</private> none label searchStrings <imports></imports> <category>Browser-Environments</category> <attributes> <package>Browser-Environments</package> </attributes> </class> <class> <name>CompiledCode</name> <environment>Kernel</environment> <super>Core.Object</super> <private>false</private> objects bytes <imports> private Kernel.OpcodePool.* </imports> <category>Kernel-Methods</category> <attributes> <package>Kernel-Methods</package> </attributes> </class> <class> <name>CompiledMethod</name> <environment>Kernel</environment> <super>Kernel.CompiledMainBlock</super> <private>false</private> objects mclass sourceCode <imports> private Kernel.OpcodePool.* </imports> <category>Kernel-Methods</category> <attributes> <package>Kernel-Methods</package> </attributes> </class> <class> <name>DeferredBinding</name> <environment>Kernel</environment> <super>Core.Magnitude</super> <private>false</private> none key resolved resolvedValue method <imports></imports> <category>Collections-Support</category> <attributes> <package>Collections-Support</package> </attributes> </class> <class> <name>BrowserEnvironmentWrapper</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.BrowserEnvironment</super> <private>false</private> none environment <imports></imports> <category>Browser-Environments</category> <attributes> <package>Browser-Environments</package> </attributes> </class> <class> <name>AndEnvironment</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.BrowserEnvironmentWrapper</super> <private>false</private> none andedEnvironment <imports></imports> <category>Browser-Environments</category> <attributes> <package>Browser-Environments</package> </attributes> </class> <class> <name>VariableBinding</name> <environment>Core</environment> <super>Core.LookupKey</super> <private>false</private> none value usage category <imports></imports> <category>Collections-Support</category> <attributes> <package>Collections-Support</package> </attributes> </class> <class> <name>SelectorEnvironment</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.BrowserEnvironmentWrapper</super> <private>false</private> none classSelectors metaClassSelectors initializers <imports></imports> <category>Browser-Environments</category> <attributes> <package>Browser-Environments</package> </attributes> </class> <class> <name>VariableEnvironment</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.BrowserEnvironmentWrapper</super> <private>false</private> none instanceVariables instanceVariableReaders instanceVariableWriters selectorCache <imports></imports> <category>Browser-Environments</category> <attributes> <package>Browser-Environments</package> </attributes> </class> <class> <name>ClassEnvironment</name> <environment>Refactory.Browser</environment> <super>Refactory.Browser.BrowserEnvironmentWrapper</super> <private>false</private> none classes metaClasses <imports></imports> <category>Browser-Environments</category> <attributes> <package>Browser-Environments</package> </attributes> </class>