Merge: Deserialization manage errors & easily create Nit object from pure JSON
[nit.git] / src / frontend / serialization_phase.nit
index 3e144ef..87b7433 100644 (file)
@@ -85,14 +85,67 @@ private class SerializationPhasePreModel
                                "Warning: duplicated annotation `{text}`.")
                end
 
-               # Add `super Serializable`
-               var sc = toolcontext.parse_superclass("Serializable")
-               sc.location = nat.location
-               nclassdef.n_propdefs.add sc
+               # Check the `serialize` state of the parent
+               if not node isa AModuledecl then
+                       var up_serialize = false
+                       var up: nullable ANode = node
+                       loop
+                               up = up.parent
+                               if up == null then
+                                       break
+                               else if up.is_serialize then
+                                       up_serialize = true
+                                       break
+                               else if up.is_noserialize then
+                                       break
+                               end
+                       end
+
+                       # Check for useless double declarations
+                       if serialize and up_serialize then
+                               toolcontext.warning(node.location, "useless-serialize",
+                                       "Warning: superfluous use of `{text}`.")
+                       else if noserialize and not up_serialize then
+                               toolcontext.warning(node.location, "useless-noserialize",
+                                       "Warning: superfluous use of `{text}`.")
+                       end
+               end
+       end
+
+       redef fun process_nclassdef(nclassdef)
+       do
+               if not nclassdef isa AStdClassdef then return
+
+               # Is there a declaration on the classdef or the module?
+               var serialize = nclassdef.is_serialize
+
+               if not serialize and not nclassdef.is_noserialize then
+                       # Is the module marked serialize?
+                       serialize = nclassdef.parent.as(AModule).is_serialize
+               end
+
+               var per_attribute = false
+               if not serialize then
+                       # Is there an attribute marked serialize?
+                       for npropdef in nclassdef.n_propdefs do
+                               if npropdef.is_serialize then
+                                       serialize = true
+                                       per_attribute = true
+                                       break
+                               end
+                       end
+               end
 
-               generate_serialization_method(nclassdef)
+               if serialize then
+                       # Add `super Serializable`
+                       var sc = toolcontext.parse_superclass("Serializable")
+                       sc.location = nclassdef.location
+                       nclassdef.n_propdefs.add sc
 
-               generate_deserialization_init(nclassdef)
+                       # Add services
+                       generate_serialization_method(nclassdef, per_attribute)
+                       generate_deserialization_init(nclassdef, per_attribute)
+               end
        end
 
        redef fun process_nmodule(nmodule)
@@ -113,7 +166,7 @@ private class SerializationPhasePreModel
                end
        end
 
-       fun generate_serialization_method(nclassdef: AClassdef)
+       fun generate_serialization_method(nclassdef: AClassdef, per_attribute: Bool)
        do
                var npropdefs = nclassdef.n_propdefs
 
@@ -123,6 +176,11 @@ private class SerializationPhasePreModel
                code.add "      super"
 
                for attribute in npropdefs do if attribute isa AAttrPropdef then
+
+                       # Is `attribute` to be skipped?
+                       if (per_attribute and not attribute.is_serialize) or
+                               attribute.is_noserialize then continue
+
                        var name = attribute.name
                        code.add "      v.serialize_attribute(\"{name}\", {name})"
                end
@@ -134,31 +192,47 @@ private class SerializationPhasePreModel
        end
 
        # Add a constructor to the automated nclassdef
-       fun generate_deserialization_init(nclassdef: AStdClassdef)
+       fun generate_deserialization_init(nclassdef: AClassdef, per_attribute: Bool)
        do
                var npropdefs = nclassdef.n_propdefs
 
                var code = new Array[String]
-               code.add "redef init from_deserializer(v: Deserializer)"
-               code.add "do"
-               code.add "      super"
-               code.add "      v.notify_of_creation self"
+               code.add """
+redef init from_deserializer(v: Deserializer)
+do
+       super
+       v.notify_of_creation self
+"""
 
                for attribute in npropdefs do if attribute isa AAttrPropdef then
+
+                       # Is `attribute` to be skipped?
+                       if (per_attribute and not attribute.is_serialize) or
+                               attribute.is_noserialize then continue
+
                        var n_type = attribute.n_type
                        var type_name
                        if n_type == null then
-                               # Use a place holder, we will replace it with the infered type after the model phases
+                               # Use a place holder, we will replace it with the inferred type after the model phases
                                type_name = toolcontext.place_holder_type_name
                        else
                                type_name = n_type.type_name
                        end
                        var name = attribute.name
 
-                       code.add ""
-                       code.add "\tvar {name} = v.deserialize_attribute(\"{name}\")"
-                       code.add "\tassert {name} isa {type_name} else print \"Unsupported type for `\{class_name\}::{name}`, got '\{{name}.class_name\}'; expected {type_name}\""
-                       code.add "\tself.{name} = {name}"
+                       code.add """
+       var {{{name}}} = v.deserialize_attribute("{{{name}}}")
+       if not {{{name}}} isa {{{type_name}}} then
+               # Check if it was a subjectent error
+               v.errors.add new AttributeTypeError("TODO remove this arg on c_src regen",
+                       self, "{{{name}}}", {{{name}}}, "{{{type_name}}}")
+
+               # Clear subjacent error
+               if v.keep_going == false then return
+       else
+               self.{{{name}}} = {{{name}}}
+       end
+"""
                end
 
                code.add "end"
@@ -185,10 +259,10 @@ private class SerializationPhasePreModel
 
                if deserializer_npropdef == null then
                        # create the property
-                       code.add "      redef fun deserialize_class(name)"
+                       code.add "      redef fun deserialize_class_intern(name)"
                        code.add "      do"
                else
-                       toolcontext.error(deserializer_npropdef.location, "Error: you cannot define `Deserializer::deserialize_class` in a module where you use `auto_serializable`.")
+                       toolcontext.error(deserializer_npropdef.location, "Error: `Deserializer::deserialize_class_intern` is generated and must not be defined, use `deserialize_class` instead.")
                        return
                end
 
@@ -281,7 +355,7 @@ redef class AModule
        private fun deserializer_nclassdef: nullable AStdClassdef
        do
                for nclassdef in n_classdefs do
-                       if nclassdef isa AStdClassdef and nclassdef.n_id.text == "Deserialization" then
+                       if nclassdef isa AStdClassdef and nclassdef.n_id.text == "Deserializer" then
                                return nclassdef
                        end
                end
@@ -290,6 +364,8 @@ redef class AModule
        end
 
        private var inits_to_retype = new Array[AMethPropdef]
+
+       redef fun is_serialize do return n_moduledecl != null and n_moduledecl.is_serialize
 end
 
 redef class AStdClassdef
@@ -297,7 +373,7 @@ redef class AStdClassdef
        do
                for npropdef in n_propdefs do if npropdef isa AMethPropdef then
                        var id = npropdef.n_methid
-                       if id isa AIdMethid and id.n_id.text == "deserialize_class" then
+                       if id isa AIdMethid and id.n_id.text == "deserialize_class_intern" then
                                return npropdef
                        end
                end