The new annotation `no_warning`, used in module, will disable specific (or all) warnings in the module.
~~~
module lexer is no_warning("missing-doc")
module parser is no_warning("missing-doc", "unread-variable")
module toto is no_warning("all")
~~~
Note: Now there is less noise, a future PR (currently in writing) will remove a lot of warnings in the `src/` directory.
Close #774
Pull-Request: #1048
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
module annotation
import modelbuilder
-private import literal
+import literal
import model::mmodule_data
redef class Prod
end
return res.first
end
-
- # Return all its annotations of a given name in the order of their declaration
- # Retun an empty array if no such an annotation.
- fun get_annotations(name: String): Array[AAnnotation]
- do
- var res = new Array[AAnnotation]
- var nas = n_annotations
- if nas == null then return res
- for na in nas.n_items do
- if na.name != name then continue
- res.add(na)
- end
- return res
- end
end
redef class AAnnotation
- # The name of the annotation
- fun name: String
- do
- return n_atid.n_id.text
- end
-
# Get the single argument of `self` as a `String`.
# Raise error and return null on any inconsistency.
fun arg_as_string(modelbuilder: ModelBuilder): nullable String
end
end
-redef class AExpr
- # Get `self` as a `String`.
- # Return null if not a string.
- fun as_string: nullable String
- do
- if not self isa AStringFormExpr then return null
- return self.value.as(not null)
- end
-
- # Get `self` as an `Int`.
- # Return null if not an integer.
- fun as_int: nullable Int
- do
- if not self isa AIntExpr then return null
- return self.value.as(not null)
- end
-
- # Get `self` as a single identifier.
- # Return null if not a single identifier.
- fun as_id: nullable String
- do
- if self isa AMethidExpr then
- return self.collect_text
- end
- if not self isa ACallExpr then return null
- if not self.n_expr isa AImplicitSelfExpr then return null
- if not self.n_args.n_exprs.is_empty then return null
- return self.n_id.text
- end
-end
-
redef class ModelBuilder
# Collect all annotations by `name` assocated to `mmodule` and its imported modules.
# Note that visibility is not considered.
abstract
intern
extern
+no_warning
pkgconfig
c_compiler_option
# Collect and orchestration of main frontend phases
module frontend
+import no_warning
import simple_misc_analysis
import literal
import modelize
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Fill toolcontext information about blacklisting of warnings.
+module no_warning
+
+import modelbuilder
+private import literal
+
+redef class ToolContext
+ # The phase should be executed before any warning on the module is processed.
+ var no_warning_phase: Phase = new NoWarningPhase(self, [literal_phase])
+end
+
+private class NoWarningPhase
+ super Phase
+
+ redef fun process_nmodule(nmodule)
+ do
+ # Get the mmodule
+ var mmodule = nmodule.mmodule
+ assert mmodule != null
+
+ # If no decl block then quit
+ var nmoduledecl = nmodule.n_moduledecl
+ if nmoduledecl == null then return
+
+ var modelbuilder = toolcontext.modelbuilder
+
+ # Get all the new annotations
+ var name = "no_warning"
+ var annots = nmoduledecl.get_annotations(name)
+
+ if annots.is_empty then return
+
+ var source = nmodule.location.file
+ if source == null then
+ modelbuilder.warning(annots.first, "file-less-module", "Warning: annotation `{name}` does not currently work on file-less modules.")
+ return
+ end
+
+ for annot in annots do
+ var args = annot.n_args
+ if args.is_empty then
+ modelbuilder.error(annot, "Annotation error: `{name}` needs a list of warnings. Use `\"all\"` to disable all warnings.")
+ continue
+ end
+ for arg in args do
+ var tag = arg.as_string
+ if tag == null then
+ modelbuilder.error(arg, "Annotation error: `{name}` expects String as arguments.")
+ continue
+ end
+
+ toolcontext.warning_blacklist[source].add(tag)
+ end
+ end
+ end
+end
private fun accept_literal(v: LiteralVisitor) do end
end
+redef class AExpr
+ # Get `self` as a `String`.
+ # Return null if not a string.
+ fun as_string: nullable String
+ do
+ if not self isa AStringFormExpr then return null
+ return self.value.as(not null)
+ end
+
+ # Get `self` as an `Int`.
+ # Return null if not an integer.
+ fun as_int: nullable Int
+ do
+ if not self isa AIntExpr then return null
+ return self.value.as(not null)
+ end
+
+ # Get `self` as a single identifier.
+ # Return null if not a single identifier.
+ fun as_id: nullable String
+ do
+ if self isa AMethidExpr then
+ return self.collect_text
+ end
+ if not self isa ACallExpr then return null
+ if not self.n_expr isa AImplicitSelfExpr then return null
+ if not self.n_args.n_exprs.is_empty then return null
+ return self.n_id.text
+ end
+end
+
+
redef class AIntExpr
# The value of the literal int once computed.
var value: nullable Int
# Lexer and its tokens.
# This file was generated by SableCC (http://www.sablecc.org/).
-module lexer
+module lexer is no_warning("missing-doc")
intrude import parser_nodes
intrude import lexer_work
# Parser.
# This file was generated by SableCC (http://www.sablecc.org/).
-module parser
+module parser is no_warning("missing-doc", "unread-variable")
intrude import parser_prod
intrude import parser_work
# Raw AST node hierarchy.
# This file was generated by SableCC (http://www.sablecc.org/).
-module parser_abs
+module parser_abs is no_warning("missing-doc")
import location
# Visit all nodes in order.
# Thus, call `v.enter_visit(e)` for each child `e`
fun visit_all(v: Visitor) is abstract
+
+ # Do a deep search and return an array of tokens that match a given text
+ fun collect_tokens_by_text(text: String): Array[Token]
+ do
+ var v = new CollectTokensByTextVisitor(text)
+ v.enter_visit(self)
+ return v.result
+ end
+
+ # Do a deep search and return an array of node that are annotated
+ # The attached node can be retrieved by two invocations of parent
+ fun collect_annotations_by_name(name: String): Array[AAnnotation]
+ do
+ var v = new CollectAnnotationsByNameVisitor(name)
+ v.enter_visit(self)
+ return v.result
+ end
end
+private class CollectTokensByTextVisitor
+ super Visitor
+ var text: String
+ var result = new Array[Token]
+ redef fun visit(node)
+ do
+ node.visit_all(self)
+ if node isa Token and node.text == text then result.add(node)
+ end
+end
+
+private class CollectAnnotationsByNameVisitor
+ super Visitor
+ var name: String
+ var result = new Array[AAnnotation]
+ redef fun visit(node)
+ do
+ node.visit_all(self)
+ if node isa AAnnotation and node.n_atid.n_id.text == name then result.add(node)
+ end
+end
+
+
# A sequence of nodes
# It is a specific class (instead of using a Array) to track the parent/child relation when nodes are added or removed
class ANodes[E: ANode]
# All the annotations attached directly to the node
var n_annotations: nullable AAnnotations = null is writable
+ # Return all its annotations of a given name in the order of their declaration
+ # Retun an empty array if no such an annotation.
+ fun get_annotations(name: String): Array[AAnnotation]
+ do
+ var res = new Array[AAnnotation]
+ var nas = n_annotations
+ if nas == null then return res
+ for na in nas.n_items do
+ if na.name != name then continue
+ res.add(na)
+ end
+ return res
+ end
+
redef fun replace_with(n: ANode)
do
super
var n_opar: nullable TOpar = null is writable
var n_args = new ANodes[AExpr](self)
var n_cpar: nullable TCpar = null is writable
+
+ # The name of the annotation
+ fun name: String
+ do
+ return n_atid.n_id.text
+ end
end
# An annotation name
# Production AST nodes full definition.
# This file was generated by SableCC (http://www.sablecc.org/).
-module parser_prod
+module parser_prod is no_warning("missing-doc")
import lexer
intrude import parser_nodes
$ output 'parser_abs.nit'
# Raw AST node hierarchy.
# This file was generated by SableCC (http://www.sablecc.org/).
-module parser_abs
+module parser_abs is no_warning("missing-doc")
import location
$ output 'lexer.nit'
# Lexer and its tokens.
# This file was generated by SableCC (http://www.sablecc.org/).
-module lexer
+module lexer is no_warning("missing-doc")
$ if $usermodule
intrude import $usermodule
$ output 'parser_prod.nit'
# Production AST nodes full definition.
# This file was generated by SableCC (http://www.sablecc.org/).
-module parser_prod
+module parser_prod is no_warning("missing-doc")
import lexer
$ if $usermodule
$ output 'parser.nit'
# Parser.
# This file was generated by SableCC (http://www.sablecc.org/).
-module parser
+module parser is no_warning("missing-doc", "unread-variable")
intrude import parser_prod
intrude import parser_work
return tok
end
end
-
-redef class ANode
- # Do a deep search and return an array of tokens that match a given text
- fun collect_tokens_by_text(text: String): Array[Token]
- do
- var v = new CollectTokensByTextVisitor(text)
- v.enter_visit(self)
- return v.result
- end
-
- # Do a deep search and return an array of node that are annotated
- # The attached node can be retrieved by two invocation of parent
- fun collect_annotations_by_name(name: String): Array[AAnnotation]
- do
- var v = new CollectAnnotationsByNameVisitor(name)
- v.enter_visit(self)
- return v.result
- end
-end
-
-private class CollectTokensByTextVisitor
- super Visitor
- var text: String
- var result = new Array[Token]
- redef fun visit(node)
- do
- node.visit_all(self)
- if node isa Token and node.text == text then result.add(node)
- end
-end
-
-private class CollectAnnotationsByNameVisitor
- super Visitor
- var name: String
- var result = new Array[AAnnotation]
- redef fun visit(node)
- do
- node.visit_all(self)
- if node isa AAnnotation and node.n_atid.n_id.text == name then result.add(node)
- end
-end
import location
import version
import template
+import more_collections
# A warning or an error
class Message
# Set this value to `true` if you need to keep the program going in case of error.
var keep_going = false is writable
+ # List of tags per source-file whose warnings are not displayed.
+ #
+ # Initially empty, it is up to the toll to fill it.
+ # The tag "all" means all warnings and advices.
+ var warning_blacklist = new MultiHashMap[SourceFile, String]
+
+ # Is the source-file of `l` associated with `tag` in `warning_blacklist`?
+ #
+ # currently returns `false` if `l` is null or does not have a source-file.
+ fun is_warning_blacklisted(l: nullable Location, tag: String): Bool
+ do
+ if l == null then return false
+ var f = l.file
+ if f == null then return false
+ var tags = warning_blacklist.get_or_null(f)
+ if tags == null then return false
+ return tags.has("all") or tags.has(tag)
+ end
+
# Output all current stacked messages and display total error informations
#
# Return true if no errors occurred.
do
if opt_warning.value.has("no-{tag}") then return
if not opt_warning.value.has(tag) and opt_warn.value == 0 then return
+ if is_warning_blacklisted(l, tag) then return
messages.add(new Message(l, tag, text))
warning_count = warning_count + 1
if opt_stop_on_first_error.value then check_errors
do
if opt_warning.value.has("no-{tag}") then return
if not opt_warning.value.has(tag) and opt_warn.value <= 1 then return
+ if is_warning_blacklisted(l, tag) then return
messages.add(new Message(l, tag, text))
warning_count = warning_count + 1
if opt_stop_on_first_error.value then check_errors