jan@71: "{ Package: 'stx:goodies/builder/reports' }" jan@71: jan@71: "{ NameSpace: Builder }" jan@71: jan@71: Report subclass:#LintReport jan@71: instanceVariableNames:'environment rules' jan@71: classVariableNames:'' jan@71: poolDictionaries:'' jan@71: category:'Builder-Reports' jan@71: ! jan@71: jan@71: jan@71: !LintReport methodsFor:'accessing - defaults'! jan@71: jan@71: defaultFileSuffix jan@71: jan@71: ^ 'Lint' jan@71: jan@71: "Modified: / 08-10-2011 / 10:49:30 / Jan Vrany " jan@71: ! jan@71: jan@71: defaultFormat jan@71: "raise an error: must be redefined in concrete subclass(es)" jan@71: jan@71: ^ LintReportFormat::PMD new jan@71: jan@71: "Modified: / 25-11-2011 / 22:06:33 / Jan Vrany " jan@79: ! jan@79: jan@79: defaultName jan@79: jan@79: environment isNil ifTrue:[^super defaultName]. jan@79: ^environment label jan@79: jan@79: "Modified: / 25-11-2011 / 22:06:33 / Jan Vrany " jan@79: "Created: / 13-01-2012 / 12:43:07 / Jan Vrany " jan@71: ! ! jan@71: jan@117: !LintReport methodsFor:'command line options'! jan@117: jan@117: cmdlineOptionRuleset jan@117: jan@117: ^CmdLineOption new jan@117: short: $s; jan@117: long: 'ruleset'; jan@233: description: 'defines set of rules to check against.'; jan@117: action:[:option | jan@117: self setupRulesFrom: option. jan@117: ]; jan@117: yourself jan@117: jan@117: "Created: / 28-02-2013 / 23:13:57 / Jan Vrany " jan@233: "Modified: / 27-05-2014 / 16:54:26 / Jan Vrany " jan@117: ! ! jan@117: jan@71: !LintReport methodsFor:'generating'! jan@71: jan@254: generateClass:class inPackage:package jan@267: | sourceInfo | jan@71: jan@254: sourceInfo := ReportSourceInfo forClass:class inPackage:package. jan@267: format writeFile:(sourceInfo pathNameAbsolute: true) jan@254: with:[ jan@254: self generateClass:class source:sourceInfo inPackage: package. jan@254: self generateClass:class class source:sourceInfo inPackage: package. jan@71: ]. jan@71: jan@254: "Created: / 15-12-2014 / 10:46:23 / Jan Vrany " jan@267: "Modified: / 16-12-2014 / 10:38:41 / Jan Vrany " jan@71: ! jan@71: jan@118: generateClass: aClass selector: aSelector source: sourceInfo jan@118: | matching | jan@71: matching := rules select: [ :each | jan@71: (self isSelectorEnvironment: each result) jan@71: and: [ each result includesSelector: aSelector in: aClass ] ]. jan@118: self generateViolations: matching class: aClass selector: aSelector source: sourceInfo jan@71: jan@71: "Created: / 07-10-2011 / 11:04:12 / Jan Vrany " jan@118: "Modified: / 01-03-2013 / 18:10:21 / Jan Vrany " jan@71: ! jan@71: jan@254: generateClass: aClass selector: aSelector source: sourceInfo inPackage: package jan@118: | matching | jan@254: matching := rules select: [ :each | jan@254: (self isSelectorEnvironment: each result) jan@254: and: [ each result includesSelector: aSelector in: aClass ] ]. jan@254: self generateViolations: matching class: aClass selector: aSelector source: sourceInfo jan@254: jan@254: "Created: / 15-12-2014 / 11:04:07 / Jan Vrany " jan@254: ! jan@254: jan@254: generateClass: aClass source: sourceInfo inPackage: package jan@254: jan@254: | matching | jan@254: (environment definesClass: aClass) ifTrue: [ jan@254: matching := rules select: [ :rule | (self isClassEnvironment: rule result) and: [ rule result includesClass: aClass ] ]. jan@254: self generateViolations: matching class: aClass source: sourceInfo jan@254: ]. jan@254: (environment selectorsForClass: aClass) asSortedCollection do: [ :selector | jan@254: self generateClass: aClass selector: selector source: sourceInfo inPackage: package jan@254: ] jan@254: jan@254: "Created: / 15-12-2014 / 11:05:10 / Jan Vrany " jan@254: ! jan@254: jan@254: generateClassesInPackage:package jan@254: (ProjectDefinition searchForClassesWithProject:package) do:[:cls | jan@254: self generateClass:cls inPackage:package jan@254: ]. jan@71: jan@254: "Created: / 15-12-2014 / 10:52:42 / Jan Vrany " jan@254: ! jan@254: jan@254: generateExtensionsInPackage:package jan@267: | sourceInfo | jan@254: jan@254: sourceInfo := ReportSourceInfo forExtensionsInPackage:package. jan@267: format writeFile:(sourceInfo pathNameAbsolute: true) jan@254: with:[ jan@254: (ProjectDefinition searchForExtensionsWithProject:package) do:[:method | jan@254: self jan@254: generateClass:method mclass jan@254: selector:method selector jan@254: source:sourceInfo jan@254: inPackage: package. jan@254: ]. jan@254: ]. jan@254: jan@254: "Created: / 15-12-2014 / 10:50:14 / Jan Vrany " jan@267: "Modified: / 16-12-2014 / 10:38:32 / Jan Vrany " jan@254: ! jan@254: jan@254: generatePackage: package jan@254: self generateClassesInPackage:package. jan@254: self generateExtensionsInPackage:package. jan@254: jan@254: "Created: / 15-12-2014 / 10:42:34 / Jan Vrany " jan@71: ! jan@71: jan@118: generateViolations: aCollection class: aClass selector: aSelector source: sourceInfo jan@118: | method offset | jan@71: jan@254: (AbstractSourceCodeManager isVersionMethodSelector: aSelector) ifTrue:[ ^ self ]. jan@254: (AbstractSourceCodeManager isExtensionsVersionMethodSelector: aSelector) ifTrue:[ ^ self ]. jan@254: jan@254: jan@118: method := aClass compiledMethodAt: aSelector. jan@118: offset := sourceInfo offsetOfMethod: method. jan@71: aCollection do: [ :rule | jan@254: | tree | jan@254: jan@254: tree := RBParser parseMethod: method source. jan@254: rule result selectionIntervalsForSource: method source tree: tree in: method mclass do:[:intervalOrNil | jan@254: | interval start stop | jan@254: jan@254: interval := interval isNil ifTrue: [ 1 to: method source size ]. jan@254: start := sourceInfo lineAndColumnOfOffset: offset + interval first - 1. jan@254: stop := sourceInfo lineAndColumnOfOffset: offset + interval last - 1. jan@254: jan@254: format writeViolation: rule jan@254: class: aClass selector: aSelector jan@254: startLine: start x column: start y jan@254: stopLine: stop x column: stop y. jan@254: ] jan@254: ] jan@254: jan@254: "Created: / 01-03-2013 / 18:05:11 / Jan Vrany " jan@254: "Modified: / 15-12-2014 / 11:32:04 / Jan Vrany " jan@254: ! jan@254: jan@254: generateViolations: aCollection class: aClass source: sourceInfo jan@254: | method offset start stop | jan@254: jan@254: start := sourceInfo lineAndColumnOfOffset: 1. jan@254: stop := sourceInfo lineAndColumnOfOffset: SmallInteger maxVal. jan@254: aCollection do: [ :rule | jan@254: | interval | jan@71: jan@71: format writeViolation: rule jan@254: class: aClass selector: nil jan@118: startLine: start x column: start y jan@118: stopLine: stop x column: stop y. jan@71: ] jan@71: jan@254: "Created: / 15-12-2014 / 11:13:47 / Jan Vrany " jan@71: ! ! jan@71: jan@71: !LintReport methodsFor:'initialization'! jan@71: jan@71: setupForClasses: classes jan@71: jan@71: environment := BrowserEnvironment new forClasses: classes. jan@71: environment label: name jan@71: jan@71: "Created: / 04-08-2011 / 14:40:31 / Jan Vrany " jan@71: ! jan@71: jan@254: setupForPackages: pkgs jan@254: pkgs isEmpty ifTrue:[^self]. jan@71: environment := PackageEnvironment jan@71: onEnvironment: BrowserEnvironment new jan@254: packageNames: pkgs. jan@79: name isNil ifTrue:[ jan@254: pkgs size > 1 ifTrue:[ jan@254: name := pkgs size printString , ' packages'. jan@79: ] ifFalse:[ jan@254: name := pkgs anElement jan@79: ] jan@79: ]. jan@71: jan@71: environment label: name. jan@71: jan@71: " jan@71: LintReport runPackage:'stx:goodies/monticello'. jan@71: LintReport runPackage:'stx:libjava' jan@71: " jan@71: jan@71: "Created: / 04-08-2011 / 14:40:06 / Jan Vrany " jan@254: "Modified: / 15-12-2014 / 10:52:15 / Jan Vrany " jan@71: ! ! jan@71: jan@71: !LintReport methodsFor:'private'! jan@71: jan@71: lineAndColumn: aString at: anInteger jan@71: | line last stream | jan@71: line := 1. jan@71: last := 0. jan@71: stream := aString readStream. jan@71: [ (stream nextLine isNil or: [ anInteger <= stream position ]) jan@71: ifTrue: [ ^ line @ (anInteger - last) ]. jan@71: last := stream position. jan@71: line := line + 1 ] jan@71: repeat jan@71: ! jan@71: jan@254: sourceFilenameForClass:class package: package jan@254: | fn cls | jan@71: jan@254: cls := class theNonMetaclass. jan@254: fn := package copy jan@254: replaceAll:$: with:$_; jan@254: replaceAll:$/ with:$_; jan@254: yourself. jan@254: fn := fn , '_' , (cls asString copyReplaceAll:$: with:$_) , '.' jan@254: , cls programmingLanguage sourceFileSuffix. jan@254: ^ self encodeFilename:fn. jan@71: jan@71: " jan@71: Builder::LintReportFormat::CheckStyle basicNew jan@71: sourceFilenameFor: Class jan@71: jan@71: Builder::LintReportFormat::CheckStyle basicNew jan@254: sourceFilenameFor: Builder::LintReportFormat" jan@254: jan@254: "Created: / 15-12-2014 / 10:46:56 / Jan Vrany " jan@254: ! jan@254: jan@254: sourceFilenameForExtensionsInPackage: package jan@254: | fn | jan@71: jan@254: fn := package copy jan@254: replaceAll:$: with:$_; jan@254: replaceAll:$/ with:$_; jan@254: yourself. jan@254: fn := fn , '_' , 'extensions.st'. jan@254: ^ self encodeFilename:fn. jan@254: jan@254: " jan@254: Builder::LintReportFormat::CheckStyle basicNew jan@254: sourceFilenameFor: Class jan@254: jan@254: Builder::LintReportFormat::CheckStyle basicNew jan@254: sourceFilenameFor: Builder::LintReportFormat" jan@254: jan@254: "Created: / 15-12-2014 / 10:50:57 / Jan Vrany " jan@71: ! ! jan@71: jan@71: !LintReport methodsFor:'running'! jan@71: jan@71: runReport jan@82: | wasTryLocalSources | jan@71: jan@82: [ jan@82: wasTryLocalSources := Class tryLocalSourceFirst. jan@84: Class tryLocalSourceFirst: true. jan@82: SmalllintChecker jan@82: runRule: (RBCompositeLintRule rules: rules) jan@82: onEnvironment: environment. jan@254: environment packageNames do:[:packageName | jan@254: self generatePackage: packageName jan@254: ]. jan@82: ] ensure:[ jan@82: Class tryLocalSourceFirst: wasTryLocalSources jan@82: ] jan@71: jan@254: "Modified: / 15-12-2014 / 11:06:27 / Jan Vrany " jan@71: ! jan@71: jan@71: setUp jan@71: jan@71: super setUp. jan@117: rules isNil ifTrue:[ jan@254: rules := RBBuiltinRuleSet rulesetBuiltinDefault flatten jan@117: ]. jan@71: jan@71: "Created: / 04-08-2011 / 14:35:27 / Jan Vrany " jan@254: "Modified: / 15-12-2014 / 11:35:46 / Jan Vrany " jan@117: ! jan@117: jan@117: setupRulesFrom: filename jan@117: | file | jan@117: file := filename asFilename. jan@117: file readingFileDo:[:s| jan@117: | spec | jan@117: spec := Parser parseLiteralArray: s. jan@117: rules := spec decodeAsLiteralArray rules. jan@117: ] jan@117: jan@117: "Created: / 28-02-2013 / 23:17:41 / Jan Vrany " jan@71: ! ! jan@71: jan@71: !LintReport methodsFor:'testing'! jan@71: jan@71: isClassEnvironment: anEnvironment jan@71: ^ #(CategoryEnvironment ClassEnvironment VariableEnvironment) includes: anEnvironment class name jan@71: ! jan@71: jan@71: isSelectorEnvironment: anEnvironment jan@71: ^ #(SelectorEnvironment ParseTreeEnvironment VariableEnvironment) includes: anEnvironment class name jan@71: ! ! jan@71: jan@71: !LintReport class methodsFor:'documentation'! jan@71: jan@71: version jan@71: ^ '$Header$' jan@71: ! jan@71: jan@71: version_CVS jan@71: ^ '$Header$' jan@71: ! jan@71: jan@71: version_SVN jan@162: ^ '$Id$' jan@71: ! ! jan@71: