reports/Builder__LintReport.st
author sr
Tue, 10 Oct 2017 15:03:59 +0200
changeset 388 c972ed7d867a
parent 267 9f55d8893118
child 570 9c47ccc9e9b5
permissions -rw-r--r--
class: RunUnitTestsStart class changed: #main:

"{ Package: 'stx:goodies/builder/reports' }"

"{ NameSpace: Builder }"

Report subclass:#LintReport
	instanceVariableNames:'environment rules'
	classVariableNames:''
	poolDictionaries:''
	category:'Builder-Reports'
!


!LintReport methodsFor:'accessing - defaults'!

defaultFileSuffix

    ^ 'Lint'

    "Modified: / 08-10-2011 / 10:49:30 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

defaultFormat
    "raise an error: must be redefined in concrete subclass(es)"

    ^ LintReportFormat::PMD new

    "Modified: / 25-11-2011 / 22:06:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

defaultName

    environment isNil ifTrue:[^super defaultName].
    ^environment label

    "Modified: / 25-11-2011 / 22:06:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Created: / 13-01-2012 / 12:43:07 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!LintReport methodsFor:'command line options'!

cmdlineOptionRuleset

    ^CmdLineOption new
        short: $s;
        long: 'ruleset';
        description: 'defines set of rules to check against.';
        action:[:option |
            self setupRulesFrom: option.
        ];
        yourself

    "Created: / 28-02-2013 / 23:13:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 27-05-2014 / 16:54:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!LintReport methodsFor:'generating'!

generateClass:class inPackage:package 
    | sourceInfo |

    sourceInfo := ReportSourceInfo forClass:class inPackage:package.
    format writeFile:(sourceInfo pathNameAbsolute: true)
        with:[
            self generateClass:class source:sourceInfo inPackage: package.
            self generateClass:class class source:sourceInfo inPackage: package.
        ].

    "Created: / 15-12-2014 / 10:46:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 16-12-2014 / 10:38:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

generateClass: aClass selector: aSelector source: sourceInfo
        | matching |
        matching := rules select: [ :each | 
                (self isSelectorEnvironment: each result)
                        and: [ each result includesSelector: aSelector in: aClass ] ].
        self generateViolations: matching class: aClass selector: aSelector source: sourceInfo

    "Created: / 07-10-2011 / 11:04:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 01-03-2013 / 18:10:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

generateClass: aClass selector: aSelector source: sourceInfo inPackage: package
        | matching |
        matching := rules select: [ :each | 
                (self isSelectorEnvironment: each result)
                        and: [ each result includesSelector: aSelector in: aClass ] ].
        self generateViolations: matching class: aClass selector: aSelector source: sourceInfo

    "Created: / 15-12-2014 / 11:04:07 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

generateClass: aClass source: sourceInfo inPackage: package 

    | matching |
    (environment definesClass: aClass) ifTrue: [
        matching := rules select: [ :rule | (self isClassEnvironment: rule result) and: [ rule result includesClass: aClass ] ].
        self generateViolations: matching class: aClass source: sourceInfo
    ].
    (environment selectorsForClass: aClass) asSortedCollection do: [ :selector | 
        self generateClass: aClass selector: selector source: sourceInfo inPackage: package
    ]

    "Created: / 15-12-2014 / 11:05:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

generateClassesInPackage:package 
    (ProjectDefinition searchForClassesWithProject:package) do:[:cls | 
        self generateClass:cls inPackage:package
    ].

    "Created: / 15-12-2014 / 10:52:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

generateExtensionsInPackage:package 
    | sourceInfo |

    sourceInfo := ReportSourceInfo forExtensionsInPackage:package.
    format writeFile:(sourceInfo pathNameAbsolute: true)
        with:[
            (ProjectDefinition searchForExtensionsWithProject:package) do:[:method | 
                self 
                    generateClass:method mclass
                    selector:method selector
                    source:sourceInfo
                    inPackage: package.
            ].
        ].

    "Created: / 15-12-2014 / 10:50:14 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 16-12-2014 / 10:38:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

generatePackage: package
    self generateClassesInPackage:package.  
    self generateExtensionsInPackage:package.

    "Created: / 15-12-2014 / 10:42:34 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

generateViolations: aCollection class: aClass selector: aSelector source: sourceInfo
    | method offset  |

    (AbstractSourceCodeManager isVersionMethodSelector: aSelector) ifTrue:[ ^ self ].
    (AbstractSourceCodeManager isExtensionsVersionMethodSelector: aSelector) ifTrue:[ ^ self ].


    method := aClass compiledMethodAt: aSelector.
    offset := sourceInfo offsetOfMethod: method.
    aCollection do: [ :rule |
        | tree |

        tree := RBParser parseMethod: method source.
        rule result selectionIntervalsForSource: method source tree: tree in: method mclass do:[:intervalOrNil | 
            | interval start stop |

            interval := interval isNil ifTrue: [ 1 to: method source size ].
            start := sourceInfo lineAndColumnOfOffset: offset + interval first - 1.
            stop  := sourceInfo lineAndColumnOfOffset: offset + interval last - 1.

            format writeViolation: rule
                       class: aClass selector: aSelector
                   startLine: start x column: start y
                    stopLine: stop x  column: stop  y.
        ]
    ]

    "Created: / 01-03-2013 / 18:05:11 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 15-12-2014 / 11:32:04 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

generateViolations: aCollection class: aClass source: sourceInfo
    | method offset start stop |

    start := sourceInfo lineAndColumnOfOffset: 1.
    stop  := sourceInfo lineAndColumnOfOffset: SmallInteger maxVal.
    aCollection do: [ :rule |
        | interval  |

        format writeViolation: rule
                   class: aClass selector: nil
               startLine: start x column: start y
                stopLine: stop x  column: stop  y.
    ]

    "Created: / 15-12-2014 / 11:13:47 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!LintReport methodsFor:'initialization'!

setupForClasses: classes

    environment := BrowserEnvironment new forClasses: classes.
    environment label: name

    "Created: / 04-08-2011 / 14:40:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

setupForPackages: pkgs
    pkgs isEmpty ifTrue:[^self].
    environment := PackageEnvironment 
                    onEnvironment: BrowserEnvironment new
                    packageNames: pkgs.
    name isNil ifTrue:[
        pkgs size > 1 ifTrue:[
            name :=  pkgs size printString , ' packages'.
        ] ifFalse:[
            name :=  pkgs anElement
        ]
    ].

    environment label: name.

    "
        LintReport runPackage:'stx:goodies/monticello'.
        LintReport runPackage:'stx:libjava'
    "

    "Created: / 04-08-2011 / 14:40:06 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 15-12-2014 / 10:52:15 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!LintReport methodsFor:'private'!

lineAndColumn: aString at: anInteger
	| line last stream |
	line := 1.
	last := 0.
	stream := aString readStream.
	[ (stream nextLine isNil or: [ anInteger <= stream position ])
		ifTrue: [ ^ line @ (anInteger - last) ].
	last := stream position.
	line := line + 1 ]
		repeat
!

sourceFilenameForClass:class package: package 
    | fn  cls |

    cls := class theNonMetaclass.
    fn := package copy
            replaceAll:$: with:$_;
            replaceAll:$/ with:$_;
            yourself.
    fn := fn , '_' , (cls asString copyReplaceAll:$: with:$_) , '.' 
            , cls programmingLanguage sourceFileSuffix.
    ^ self encodeFilename:fn.

    "
        Builder::LintReportFormat::CheckStyle basicNew
            sourceFilenameFor: Class

        Builder::LintReportFormat::CheckStyle basicNew
            sourceFilenameFor: Builder::LintReportFormat"

    "Created: / 15-12-2014 / 10:46:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

sourceFilenameForExtensionsInPackage: package 
    | fn |

    fn := package copy
            replaceAll:$: with:$_;
            replaceAll:$/ with:$_;
            yourself.
    fn := fn , '_' , 'extensions.st'.
    ^ self encodeFilename:fn.

    "
        Builder::LintReportFormat::CheckStyle basicNew
            sourceFilenameFor: Class

        Builder::LintReportFormat::CheckStyle basicNew
            sourceFilenameFor: Builder::LintReportFormat"

    "Created: / 15-12-2014 / 10:50:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!LintReport methodsFor:'running'!

runReport
    | wasTryLocalSources |

    [
        wasTryLocalSources := Class tryLocalSourceFirst.
        Class tryLocalSourceFirst: true.
        SmalllintChecker 
            runRule: (RBCompositeLintRule rules: rules)
            onEnvironment: environment.
        environment packageNames do:[:packageName | 
            self generatePackage: packageName  
        ].
    ] ensure:[
        Class tryLocalSourceFirst: wasTryLocalSources 
    ]

    "Modified: / 15-12-2014 / 11:06:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

setUp

    super setUp.
    rules isNil ifTrue:[
        rules := RBBuiltinRuleSet rulesetBuiltinDefault flatten   
    ].

    "Created: / 04-08-2011 / 14:35:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 15-12-2014 / 11:35:46 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

setupRulesFrom: filename
    | file |    
    file := filename asFilename.
    file readingFileDo:[:s|
        | spec |
        spec := Parser parseLiteralArray: s.
        rules := spec decodeAsLiteralArray rules.
    ]

    "Created: / 28-02-2013 / 23:17:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!LintReport methodsFor:'testing'!

isClassEnvironment: anEnvironment
	^ #(CategoryEnvironment ClassEnvironment VariableEnvironment) includes: anEnvironment class name
!

isSelectorEnvironment: anEnvironment
	^ #(SelectorEnvironment ParseTreeEnvironment VariableEnvironment) includes: anEnvironment class name
! !

!LintReport class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
!

version_SVN
    ^ '$Id$'
! !