]> gitweb.factorcode.org Git - factor.git/blob - basis/help/lint/checks/checks.factor
help.lint.checks: Save lint disposables in hash.
[factor.git] / basis / help / lint / checks / checks.factor
1 ! Copyright (C) 2009 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors arrays assocs classes classes.struct
4 classes.tuple combinators combinators.short-circuit
5 combinators.smart continuations debugger definitions effects
6 eval formatting fry grouping help help.markup help.topics io
7 io.streams.string kernel macros math math.statistics namespaces
8 parser.notes prettyprint sequences sequences.deep sets splitting
9 strings summary tools.destructors unicode vocabs vocabs.loader
10 words words.constant words.symbol ;
11 IN: help.lint.checks
12
13 ERROR: simple-lint-error message ;
14
15 M: simple-lint-error summary message>> ;
16
17 M: simple-lint-error error. summary print ;
18
19 SYMBOL: vocabs-quot
20 SYMBOL: vocab-articles
21
22 : no-ui-disposables ( seq -- seq' )
23     [
24         class-of name>> {
25             "single-texture" "multi-texture" ! opengl.textures
26             "line" ! core-text
27             "layout" ! ui.text.pango
28             "script-string" ! windows.uniscribe
29         } member?
30     ] reject ;
31
32 : eval-with-stack ( str -- output )
33     [
34         [
35             parser-quiet? on parse-string [
36                 output>array [
37                     nl "--- Data stack:" print stack.
38                 ] unless-empty
39             ] call( quot -- )
40         ] [ nip print-error ] recover
41     ] with-string-writer ;
42
43 : check-example ( element -- )
44     [
45         '[
46             _ rest [
47                 but-last "\n" join
48                 eval-with-stack
49                 "\n" ?tail drop
50             ] keep
51             last assert=
52         ] vocabs-quot get call( quot -- )
53     ] leaks members no-ui-disposables
54     dup length 0 > [
55        dup [ class-of ] histogram-by
56        [ "Leaked resources: " write ... ] with-string-writer simple-lint-error
57     ] [
58         drop
59     ] if ;
60
61 : check-examples ( element -- )
62     \ $example swap elements [ check-example ] each ;
63
64 : extract-values ( element -- seq )
65     \ $values swap elements
66     [ f ] [ first rest keys ] if-empty ;
67
68 : extract-value-effects ( element -- seq )
69     \ $values swap elements [ f ] [
70         first rest [
71             \ $quotation swap elements [ f ] [
72                 first second dup effect? [ effect>string ] when
73             ] if-empty
74         ] map
75     ] if-empty ;
76
77 : effect-values ( word -- seq )
78     stack-effect
79     [ in>> ] [ out>> ] bi append
80     [ dup pair? [ first ] when effect>string ] map members ;
81
82 : effect-effects ( word -- seq )
83     stack-effect in>> [
84         dup pair?
85         [ second dup effect? [ effect>string ] [ drop f ] if ]
86         [ drop f ] if
87     ] map ;
88
89 : contains-funky-elements? ( element -- ? )
90     {
91         $shuffle
92         $complex-shuffle
93         $values-x/y
94         $predicate
95         $class-description
96         $error-description
97     } swap '[ _ elements empty? not ] any? ;
98
99 : don't-check-word? ( word -- ? )
100     {
101         [ macro? ]
102         [ symbol? ]
103         [ parsing-word? ]
104         [ "declared-effect" word-prop not ]
105         [ constant? ]
106         [ "help" word-prop not ]
107     } 1|| ;
108
109 : skip-check-values? ( word element -- ? )
110     [ don't-check-word? ] [ contains-funky-elements? ] bi* or ;
111
112 : check-values ( word element -- )
113     2dup skip-check-values? [ 2drop ] [
114         [ effect-values ] [ extract-values ] bi* 2dup
115         sequence= [ 2drop ] [
116             "$values don't match stack effect; expected %u, got %u" sprintf
117             simple-lint-error
118         ] if
119     ] if ;
120
121 : check-value-effects ( word element -- )
122     [ effect-effects ] [ extract-value-effects ] bi*
123     [ 2dup and [ = ] [ 2drop t ] if ] 2all? [
124         "$quotation stack effects in $values don't match"
125         simple-lint-error
126     ] unless ;
127
128 : check-nulls ( element -- )
129     \ $values swap elements
130     null swap deep-member?
131     [ "$values should not contain null" simple-lint-error ] when ;
132
133 : check-see-also ( element -- )
134     \ $see-also swap elements [ rest all-unique? ] all?
135     [ "$see-also are not unique" simple-lint-error ] unless ;
136
137 : check-modules ( element -- )
138     \ $vocab-link swap elements [
139         second
140         vocab-exists? [
141             "$vocab-link to non-existent vocabulary"
142             simple-lint-error
143         ] unless
144     ] each ;
145
146 : check-rendering ( element -- )
147     [ print-content ] with-string-writer drop ;
148
149 : check-strings ( str -- )
150     [
151         "\n\t" intersects? [
152             "Paragraph text should not contain \\n or \\t"
153             simple-lint-error
154         ] when
155     ] [
156         "  " swap subseq? [
157             "Paragraph text should not contain double spaces"
158             simple-lint-error
159         ] when
160     ] bi ;
161
162 : check-whitespace ( str1 str2 -- )
163     [ " " tail? ] [ " " head? ] bi* or
164     [ "Missing whitespace between strings" simple-lint-error ] unless ;
165
166 : check-bogus-nl ( element -- )
167     { { $nl } { { $nl } } } [ head? ] with any? [
168         "Simple element should not begin with a paragraph break"
169         simple-lint-error
170     ] when ;
171
172 : extract-slots ( elements -- seq )
173     [ dup pair? [ first \ $slot = ] [ drop f ] if ] deep-filter
174     [ second ] map ;
175
176 : check-class-description ( word element -- )
177     \ $class-description swap elements over class? [
178         [
179             dup struct-class? [ struct-slots ] [ all-slots ] if
180             [ name>> ] map
181         ] [ extract-slots ] bi*
182         [ swap member? ] with reject [
183             ", " join "Described $slot does not exist: " prepend
184             simple-lint-error
185         ] unless-empty
186     ] [
187         nip empty? not [
188             "A word that is not a class has a $class-description"
189             simple-lint-error
190         ] when
191     ] if ;
192
193 : check-article-title ( article -- )
194     article-title first LETTER?
195     [ "Article title must begin with a capital letter" simple-lint-error ] unless ;
196
197 : check-elements ( element -- )
198     {
199         [ check-bogus-nl ]
200         [ [ string? ] filter [ check-strings ] each ]
201         [ [ simple-element? ] filter [ check-elements ] each ]
202         [ 2 <clumps> [ [ string? ] all? ] filter [ first2 check-whitespace ] each ]
203     } cleave ;
204
205 : check-descriptions ( element -- )
206     { $description $class-description $var-description }
207     swap '[
208         _ elements [
209             rest { { } { "" } } member?
210             [ "Empty $description" simple-lint-error ] when
211         ] each
212     ] each ;
213
214 : check-markup ( element -- )
215     {
216         [ check-elements ]
217         [ check-rendering ]
218         [ check-examples ]
219         [ check-modules ]
220         [ check-descriptions ]
221     } cleave ;
222
223 : files>vocabs ( -- assoc )
224     loaded-vocab-names
225     [ [ [ vocab-docs-path ] keep ] H{ } map>assoc ]
226     [ [ [ vocab-source-path ] keep ] H{ } map>assoc ]
227     bi assoc-union ;
228
229 : group-articles ( -- assoc )
230     articles get keys
231     files>vocabs
232     H{ } clone [
233         '[
234             dup >link where dup
235             [ first _ at _ push-at ] [ 2drop ] if
236         ] each
237     ] keep ;
238
239 : all-word-help ( words -- seq )
240     [ word-help ] filter ;