Module to build contract

This module provide extension of the modele to add contracts, the building phase and the "re-driving" to call the contract.

FIXME Split the module in three parts: extension of the modele, building phase and the "re-driving"

Introduced classes

private class CallSiteVisitor

nitc :: CallSiteVisitor

This visitor checks the callsite to see if the target mpropdef has a contract.
private class ContractsPhase

nitc :: ContractsPhase

private class ContractsVisitor

nitc :: ContractsVisitor

Visitor to build all contracts.

Redefined classes

redef class AAnnotation

nitc :: contracts $ AAnnotation

A single annotation
redef class AMethPropdef

nitc :: contracts $ AMethPropdef

A definition of all kind of method (including constructors)
redef class AModule

nitc :: contracts $ AModule

The main node of a Nit source-file
redef class ANewExpr

nitc :: contracts $ ANewExpr

An explicit instantiation. eg new T
redef abstract class ANode

nitc :: contracts $ ANode

Root of the AST class-hierarchy
redef abstract class APropdef

nitc :: contracts $ APropdef

The definition of a property
redef abstract class ASendExpr

nitc :: contracts $ ASendExpr

A polymorphic invocation of a method
redef class ASignature

nitc :: contracts $ ASignature

A signature in a method definition. eg (x,y:X,z:Z):T
redef abstract class BottomMContract

nitc :: contracts $ BottomMContract

The root of all contracts where the call is after the execution of the original method (invariant and ensure).
redef abstract class MContract

nitc :: contracts $ MContract

The root of all contracts
redef class MEnsure

nitc :: contracts $ MEnsure

A ensure (postcondition) representation
redef class MExpect

nitc :: contracts $ MExpect

An expect (precondition) contract representation
redef class MMethod

nitc :: contracts $ MMethod

A global method
redef class MMethodDef

nitc :: contracts $ MMethodDef

A local definition of a method
redef class MSignature

nitc :: contracts $ MSignature

A signature of a method
redef class ToolContext

nitc :: contracts $ ToolContext

Global context for tools

All class definitions

redef class AAnnotation

nitc :: contracts $ AAnnotation

A single annotation
redef class AMethPropdef

nitc :: contracts $ AMethPropdef

A definition of all kind of method (including constructors)
redef class AModule

nitc :: contracts $ AModule

The main node of a Nit source-file
redef class ANewExpr

nitc :: contracts $ ANewExpr

An explicit instantiation. eg new T
redef abstract class ANode

nitc :: contracts $ ANode

Root of the AST class-hierarchy
redef abstract class APropdef

nitc :: contracts $ APropdef

The definition of a property
redef abstract class ASendExpr

nitc :: contracts $ ASendExpr

A polymorphic invocation of a method
redef class ASignature

nitc :: contracts $ ASignature

A signature in a method definition. eg (x,y:X,z:Z):T
redef abstract class BottomMContract

nitc :: contracts $ BottomMContract

The root of all contracts where the call is after the execution of the original method (invariant and ensure).
private class CallSiteVisitor

nitc $ CallSiteVisitor

This visitor checks the callsite to see if the target mpropdef has a contract.
private class ContractsPhase

nitc $ ContractsPhase

private class ContractsVisitor

nitc $ ContractsVisitor

Visitor to build all contracts.
redef abstract class MContract

nitc :: contracts $ MContract

The root of all contracts
redef class MEnsure

nitc :: contracts $ MEnsure

A ensure (postcondition) representation
redef class MExpect

nitc :: contracts $ MExpect

An expect (precondition) contract representation
redef class MMethod

nitc :: contracts $ MMethod

A global method
redef class MMethodDef

nitc :: contracts $ MMethodDef

A local definition of a method
redef class MSignature

nitc :: contracts $ MSignature

A signature of a method
redef class ToolContext

nitc :: contracts $ ToolContext

Global context for tools
package_diagram nitc::contracts contracts nitc::parse_annotations parse_annotations nitc::contracts->nitc::parse_annotations nitc\>semantize\> semantize nitc::contracts->nitc\>semantize\> nitc::model_contract model_contract nitc::contracts->nitc::model_contract nitc::astbuilder astbuilder nitc::contracts->nitc::astbuilder nitc::modelize_property modelize_property nitc::parse_annotations->nitc::modelize_property nitc nitc nitc\>semantize\>->nitc nitc\>modelize\> modelize nitc\>semantize\>->nitc\>modelize\> nitc::scope scope nitc::model_contract->nitc::scope nitc::typing typing nitc::astbuilder->nitc::typing ...nitc::modelize_property ... ...nitc::modelize_property->nitc::modelize_property ...nitc ... ...nitc->nitc ...nitc\>modelize\> ... ...nitc\>modelize\>->nitc\>modelize\> ...nitc::scope ... ...nitc::scope->nitc::scope ...nitc::typing ... ...nitc::typing->nitc::typing nitc::code_gen code_gen nitc::code_gen->nitc::contracts nitc::abstract_compiler abstract_compiler nitc::abstract_compiler->nitc::contracts nitc::nit nit nitc::nit->nitc::code_gen nitc::nitc nitc nitc::nitc->nitc::code_gen nitc::nitmetrics nitmetrics nitc::nitmetrics->nitc::code_gen nitc::nitvm nitvm nitc::nitvm->nitc::code_gen nitc::nit... ... nitc::nit...->nitc::nit nitc::nitc... ... nitc::nitc...->nitc::nitc nitc::nitmetrics... ... nitc::nitmetrics...->nitc::nitmetrics nitc::nitvm... ... nitc::nitvm...->nitc::nitvm nitc::separate_compiler separate_compiler nitc::separate_compiler->nitc::abstract_compiler nitc::global_compiler global_compiler nitc::global_compiler->nitc::abstract_compiler nitc::light light nitc::light->nitc::abstract_compiler nitc::memory_logger memory_logger nitc::memory_logger->nitc::abstract_compiler nitc::compiler_serialization compiler_serialization nitc::compiler_serialization->nitc::abstract_compiler nitc::android android nitc::android->nitc::abstract_compiler nitc::emscripten emscripten nitc::emscripten->nitc::abstract_compiler nitc::xcode_templates xcode_templates nitc::xcode_templates->nitc::abstract_compiler nitc::separate_compiler... ... nitc::separate_compiler...->nitc::separate_compiler nitc::global_compiler... ... nitc::global_compiler...->nitc::global_compiler nitc::light... ... nitc::light...->nitc::light nitc::memory_logger... ... nitc::memory_logger...->nitc::memory_logger nitc::compiler_serialization... ... nitc::compiler_serialization...->nitc::compiler_serialization nitc::android... ... nitc::android...->nitc::android nitc::emscripten... ... nitc::emscripten...->nitc::emscripten nitc::xcode_templates... ... nitc::xcode_templates...->nitc::xcode_templates

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module annotation

nitc :: annotation

Management and utilities on annotations
module array

core :: array

This module introduces the standard array structure.
module auto_super_init

nitc :: auto_super_init

Computing of super-constructors that must be implicitly called at the begin of constructors.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module console

console :: console

Defines some ANSI Terminal Control Escape Sequences.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module digraph

graph :: digraph

Implementation of directed graphs, also called digraphs.
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module flow

nitc :: flow

Intraprocedural static flow.
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module ini

ini :: ini

Read and write INI configuration files
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module lexer

nitc :: lexer

Lexer and its tokens.
module lexer_work

nitc :: lexer_work

Internal algorithm and data structures for the Nit lexer
module list

core :: list

This module handle double linked lists
module literal

nitc :: literal

Parsing of literal values in the abstract syntax tree.
module loader

nitc :: loader

Loading of Nit source files
module local_var_init

nitc :: local_var_init

Verify that local variables are initialized before their usage
module location

nitc :: location

Nit source-file and locations in source-file
module math

core :: math

Mathematical operations
module mdoc

nitc :: mdoc

Documentation of model entities
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module mmodule

nitc :: mmodule

modules and module hierarchies in the metamodel
module mmodule_data

nitc :: mmodule_data

Define and retrieve data in modules
module model

nitc :: model

Classes, types and properties
module model_base

nitc :: model_base

The abstract concept of model and related common things
module modelbuilder_base

nitc :: modelbuilder_base

Load nit source files and build the associated model
module modelize

nitc :: modelize

Create a model from nit source files
module modelize_class

nitc :: modelize_class

Analysis and verification of class definitions to instantiate model element
module modelize_property

nitc :: modelize_property

Analysis and verification of property definitions to instantiate model element
module more_collections

more_collections :: more_collections

Highly specific, but useful, collections-related classes.
module mpackage

nitc :: mpackage

Modelisation of a Nit package
module native

core :: native

Native structures for text and bytes
module nitpm_shared

nitc :: nitpm_shared

Services related to the Nit package manager
module numeric

core :: numeric

Advanced services for Numeric types
module opts

opts :: opts

Management of options on the command line
module ordered_tree

ordered_tree :: ordered_tree

Manipulation and presentation of ordered trees.
module parser

nitc :: parser

Parser.
module parser_nodes

nitc :: parser_nodes

AST nodes of the Nit language
module parser_prod

nitc :: parser_prod

Production AST nodes full definition.
module parser_work

nitc :: parser_work

Internal algorithm and data structures for the Nit parser
module phase

nitc :: phase

Phases of the processing of nit programs
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module scope

nitc :: scope

Identification and scoping of local variables and labels.
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module tables

nitc :: tables

Module that interfaces the parsing tables.
module template

template :: template

Basic template system
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module toolcontext

nitc :: toolcontext

Common command-line tool infrastructure than handle options and error messages
module typing

nitc :: typing

Intraprocedural resolution of static types and OO-services
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module version

nitc :: version

This file was generated by git-gen-version.sh

Parents

module astbuilder

nitc :: astbuilder

Instantiation and transformation of semantic nodes in the AST of expressions and statements
module model_contract

nitc :: model_contract

The abstract concept of a contract in the model
module parse_annotations

nitc :: parse_annotations

Simple annotation parsing
module semantize

nitc :: semantize

Process bodies of methods in regard with the model.

Children

module abstract_compiler

nitc :: abstract_compiler

Abstract compiler
module code_gen

nitc :: code_gen

Main frontend phases plus code generation phases

Descendants

module a_star-m

a_star-m

module android

nitc :: android

Compile program for the Android platform
module compiler

nitc :: compiler

Compilation to C
module compiler_ffi

nitc :: compiler_ffi

Full FFI support for the compiler
module compiler_serialization

nitc :: compiler_serialization

Serialization support for the compiler
module emscripten

nitc :: emscripten

Compile to JavaScript using the Emscripten SDK
module global_compiler

nitc :: global_compiler

Global compilation of a Nit program
module ios

nitc :: ios

Compile programs for the iOS platform
module light

nitc :: light

Light FFI support for the compiler
module light_only

nitc :: light_only

Compiler support for the light FFI only, detects unsupported usage of callbacks
module memory_logger

nitc :: memory_logger

Extension to inject memory-tracing instrumentation in code generated by nitc.
module nit

nitc :: nit

A naive Nit interpreter
module nitc

nitc :: nitc

A Nit compiler
module nith

nitc :: nith

A ligHt Nit compiler
module nitmetrics

nitc :: nitmetrics

A program that collects various metrics on nit programs and libraries
module nitvm

nitc :: nitvm

The Nit virtual machine launcher
module separate_compiler

nitc :: separate_compiler

Separate compilation of a Nit program
module separate_erasure_compiler

nitc :: separate_erasure_compiler

Separate compilation of a Nit program with generic type erasure
module test_astbuilder

nitc :: test_astbuilder

Program used to test the clone method of the astbuilder tool
module xcode_templates

nitc :: xcode_templates

Templates and other services to create XCode projects
# Module to build contract
# This module provide extension of the modele to add contracts, the building phase and the "re-driving" to call the contract.
#
# FIXME Split the module in three parts: extension of the modele, building phase and the "re-driving"
module contracts

import parse_annotations
import phase
import semantize
intrude import model_contract
intrude import astbuilder
intrude import modelize_property
intrude import scope
intrude import typing

redef class ToolContext
	# Parses contracts annotations.
	var contracts_phase: Phase = new ContractsPhase(self, [modelize_property_phase,typing_phase])
end

private class ContractsPhase
	super Phase

	redef fun process_nmodule(nmodule)do
		# Check if the contracts are disabled
		if toolcontext.opt_no_contract.value then return
		nmodule.do_contracts(self.toolcontext)
	end

	redef fun process_mainmodule(mainmodule: MModule, given_mmodules: SequenceRead[MModule]) do
		# Visit all loaded modules `toolcontext.nmodules` to do contract weaving
		for nmodule in toolcontext.modelbuilder.nmodules do
			nmodule.do_weaving_contracts(self.toolcontext)
		end
	end
end

redef class AModule

	# Entry point to generate the entire contract infrastructure.
	# Once this method is called we must call the `do_weaving_contracts` method (see it for more information).
	fun do_contracts(toolcontext: ToolContext) do
		var ast_builder = new ASTBuilder(mmodule.as(not null))
		#
		var contract_visitor = new ContractsVisitor(toolcontext, toolcontext.modelbuilder.identified_modules.first, self, ast_builder)
		contract_visitor.enter_visit(self)
	end

	# Entry point to execute the weaving in order to redirect the calls to the contract sides if it's needed.
	fun do_weaving_contracts(toolcontext: ToolContext)
	do
		var ast_builder = new ASTBuilder(mmodule.as(not null))
		var callsite_visitor = new CallSiteVisitor(toolcontext, ast_builder)
		callsite_visitor.enter_visit(self)
	end
end

# Visitor to build all contracts.
private class ContractsVisitor
	super Visitor

	# Instance of the toolcontext
	var toolcontext: ToolContext

	# The main module
	# It's strore to define if the contract need to be build depending on the selected policy `--no-contract` `--full-contract` or default
	var mainmodule: MModule

	# Actual visited module
	var visited_module: AModule

	var ast_builder: ASTBuilder

	# The `ASignature` of the actual build contract
	var n_signature: ASignature is noinit

	# The `MSignature` of the actual build contract
	var m_signature: MSignature is noinit

	# The `current_location` can corresponding of the annotation or method location.
	var current_location: Location is noinit

	# Is the contrat is an introduction or not?
	# This attribute has the same value as the `is_intro` of the propdef attached to the contract.
	# Note : For MClassDef `is_intro_contract == false`. This is due to the fact that a method for checking invariants is systematically added to the root object class.
	var is_intro_contract: Bool is noinit

	# Actual visited class
	var visited_class: nullable AClassdef

	# is `no_contract` annotation was found
	var find_no_contract = false

	# The reference to the `in_contract` attribute.
	# This attribute is used to disable contract verification when you are already in a contract verification.
	# Keep the `in_contract` attribute to avoid searching at each contrat
	var in_contract_attribute: nullable MAttribute = null

	redef fun visit(node)
	do
		node.create_contracts(self)
		node.visit_all(self)
	end

	# Method use to define the signature part of the method `ASignature` and `MSignature`
	# The given `mcontract` provided `adapt_nsignature` and `adapt_msignature` to copy and adapt the given signature (`nsignature` and `msignature`)
	fun define_signature(mcontract: MContract, nsignature: nullable ASignature, msignature: nullable MSignature)
	do
		if nsignature != null and msignature != null then nsignature.ret_type = msignature.return_mtype
		self.n_signature = mcontract.adapt_nsignature(nsignature)
		self.m_signature = mcontract.adapt_msignature(msignature)
	end

	# Define the new contract take in consideration that an old contract exist or not
	private fun build_contract(n_annotations: Array[AAnnotation], mcontract: MContract, mclassdef: MClassDef)
	do
		var n_conditions = new Array[AExpr]
		# Retrieving the expression provided in the annotation
		for n_annotation in n_annotations do n_conditions.add n_annotation.construct_condition(self)
		if is_intro_contract then
			# Create new contract method
			mcontract.create_intro_contract(self, n_conditions, mclassdef)
		else
			# Create a redef of contract to take in consideration the new condition
			mcontract.create_subcontract(self, n_conditions, mclassdef)
		end
	end

	# Inject the incontract attribute (`_in_contract_`) in the `Sys` class
	# This attribute allows to check if the contract need to be executed
	private fun inject_incontract_in_sys
	do
		# If the `in_contract_attribute` already know just return
		if in_contract_attribute != null then return

		var sys_class = toolcontext.modelbuilder.get_mclass_by_name(visited_module, mainmodule, "Sys")

		# Try to get the `in_contract` property, if it has already defined in a previously visited module.
		var in_contract_property = toolcontext.modelbuilder.try_get_mproperty_by_name(visited_module, sys_class.intro, "__in_contract_")
		if in_contract_property != null then
			self.in_contract_attribute = in_contract_property.as(MAttribute)
			return
		end

		var bool_false = new AFalseExpr.make(mainmodule.bool_type)
		var n_in_contract_attribute = toolcontext.modelbuilder.create_attribute_from_name("__in_contract_", sys_class.intro, mainmodule.bool_type, public_visibility).create_setter(toolcontext.modelbuilder, true).define_default(bool_false)

		in_contract_attribute = n_in_contract_attribute.mpropdef.mproperty
	end

	# Return the `_in_contract_` attribute.
	# If the attribute `_in_contract_` does not exist it's injected with `inject_incontract_in_sys`
	private fun get_incontract: MAttribute
	do
		if self.in_contract_attribute == null then inject_incontract_in_sys
		return in_contract_attribute.as(not null)
	end

	# Return an `AIfExpr` with the contract encapsulated by an `if` to check if it's already in a contract verification.
	#
	# Example:
	# ~~~nitish
	# class A
	# 	fun bar([...]) is except([...])
	#
	# 	fun _contract_bar([...])
	#	do
	#		if not sys._in_contract_ then
	#			sys._in_contract_ = true
	#			_bar_expect([...])
	#			sys._in_contract_ = false
	#		end
	#		bar([...])
	#	end
	#
	# 	fun _bar_expect([...])
	# end
	# ~~~~
	#
	private fun encapsulated_contract_call(visited_method: AMethPropdef, call_to_contracts: Array[ACallExpr]): AIfExpr
	do
		var sys_property = toolcontext.modelbuilder.model.get_mproperties_by_name("sys").first.as(MMethod)
		var callsite_sys = ast_builder.create_callsite(toolcontext.modelbuilder, visited_method, sys_property, true)

		var incontract_attribute = get_incontract

		var callsite_get_incontract = ast_builder.create_callsite(toolcontext.modelbuilder, visited_method, incontract_attribute.getter.as(MMethod), false)
		var callsite_set_incontract = ast_builder.create_callsite(toolcontext.modelbuilder, visited_method, incontract_attribute.setter.as(MMethod), false)

		var n_condition = ast_builder.make_not(ast_builder.make_call(ast_builder.make_call(new ASelfExpr, callsite_sys, null), callsite_get_incontract, null))

		var n_if = ast_builder.make_if(n_condition, null)

		var if_then_block = n_if.n_then.as(ABlockExpr)

		if_then_block.add(ast_builder.make_call(ast_builder.make_call(new ASelfExpr, callsite_sys, null), callsite_set_incontract, [new ATrueExpr.make(mainmodule.bool_type)]))
		if_then_block.add_all(call_to_contracts)
		if_then_block.add(ast_builder.make_call(ast_builder.make_call(new ASelfExpr, callsite_sys, null), callsite_set_incontract, [new AFalseExpr.make(mainmodule.bool_type)]))

		return n_if
	end
end

# This visitor checks the `callsite` to see if the target `mpropdef` has a contract.
private class CallSiteVisitor
	super Visitor

	# Instance of the toolcontext
	var toolcontext: ToolContext

	var ast_builder: ASTBuilder

	# Actual visited method
	var visited_propdef: APropdef is noinit

	redef fun visit(node)
	do
		node.check_callsite(self)
		node.visit_all(self)
	end

	# Check if the callsite is bound on a property with a contract.
	# If the property is linked to a contract a new callsite will be created towards the correct facet,
	# in the other case the returned callsite wall be the same as the given `callsite`
	private fun drive_callsite_to_contract(callsite: CallSite): CallSite
	do
		var contract_facet = callsite.mproperty.mcontract_facet
		var visited_mpropdef = visited_propdef.mpropdef

		if visited_mpropdef isa MContract or visited_mpropdef isa MFacet then return callsite
		if visited_mpropdef == null or contract_facet == null then return callsite

		return ast_builder.create_callsite(toolcontext.modelbuilder, visited_propdef, contract_facet, callsite.recv_is_self)
	end
end

redef class ANode
	private fun create_contracts(v: ContractsVisitor) do end
	private fun check_callsite(v: CallSiteVisitor) do end
end

redef class AAnnotation

	# Returns the conditions of annotation parameters. If there are several parameters, the result is an `AAndExpr`
	# Example:
	# the contract `ensure(true, i == 10, f >= 1.0)`
	# return this condition `(true and i == 10 and f >= 1.0)`
	private fun construct_condition(v : ContractsVisitor): AExpr
	do
		var n_condition = n_args.first
		n_args.remove_at(0)
		for n_arg in n_args do n_condition = v.ast_builder.make_and(n_condition, n_arg)
		n_condition.location = self.location
		return n_condition
	end
end

redef class MContract

	# Should contract be called?
	# return `true` if the contract needs to be called.
	private fun is_called(v: ContractsVisitor, mpropdef: MPropDef): Bool
	do
		return v.toolcontext.opt_full_contract.value
	end

	# Method use to diplay warning when the contract is not present at the introduction
	private fun no_intro_contract(v: ContractsVisitor, a: Array[AAnnotation])do end

	# Creating specific inheritance block contract
	#
	# `super_args` : Correspond to the `super` call arguments
	private fun create_inherit_nblock(v: ContractsVisitor, n_conditions: Array[AExpr], super_args: Array[AExpr]): ABlockExpr is abstract

	# Method to adapt the given `n_mpropdef.n_block` to the contract
	private fun adapt_method_to_contract(v: ContractsVisitor, mfacet: MFacet, n_mpropdef: AMethPropdef) is abstract

	# Create and return an adapted `MSignature` specifically for the contract in fonction of the given `m_signature`
	private fun adapt_specific_msignature(m_signature: MSignature): MSignature do return m_signature.adapt_to_contract

	# Create and return an adapted `ASignature` specifically for the contract in fonction of the given `n_signature`
	private fun adapt_specific_nsignature(n_signature: ASignature): ASignature do return n_signature.adapt_to_contract

	# Adapt the `m_signature` to the contract
	# If `m_signature == null` return a new `MSignature` else it calls a specific adapt method see `adapt_specific_msignature`
	private fun adapt_msignature(m_signature: nullable MSignature): MSignature
	do
		if m_signature == null then return new MSignature(new Array[MParameter])
		return adapt_specific_msignature(m_signature)
	end

	# Adapt the `n_signature` to the contract
	# If `n_signature == null` return a new `ASignature` else it calls a specific adapt method see `adapt_specific_nsignature`
	private fun adapt_nsignature(n_signature: nullable ASignature): ASignature
	do
		if n_signature == null then return new ASignature
		return adapt_specific_nsignature(n_signature)
	end

	# Create the initial contract (intro)
	# All contracts have the same implementation for the introduction.
	#
	# Example:
	# ~~~nitish
	# fun contrat([...])
	# do
	#	assert contract_condition
	# end
	# ~~~
	#
	private fun create_intro_contract(v: ContractsVisitor, n_conditions: Array[AExpr], mclassdef: MClassDef)
	do
		var n_block = v.ast_builder.make_block
		for n_condition in n_conditions do
			# Create a new tid to set the name of the assert for more explicit error
			var tid = new TId.init_tk(n_condition.location)
			tid.text = "{n_condition.location.text}"
			var n_assert = v.ast_builder.make_assert(tid, n_condition, null)
			# Define the assert location to reference the annoation
			n_assert.location = n_condition.location
			n_block.add n_assert
		end
		make_contract(v, n_block, mclassdef)
	end

	# Create a contract to check the old (super call) and the new conditions
	#
	# Example:
	# ~~~nitish
	# fun contrat([...])
	# do
	#	super # Call the old contracts
	#	assert new_condition
	# end
	# ~~~
	#
	private fun create_subcontract(v: ContractsVisitor, n_conditions: Array[AExpr], mclassdef: MClassDef)
	do
		var args = v.n_signature.make_parameter_read(v.ast_builder)
		var n_block = v.ast_builder.make_block
		n_block = self.create_inherit_nblock(v, n_conditions, args)
		make_contract(v, n_block, mclassdef)
	end

	# Build a new contract method with a specific block `n_block` in a specified `mclassdef`
	private fun make_contract(v: ContractsVisitor, n_block: AExpr, mclassdef: MClassDef)
	do
		var n_contractdef = v.toolcontext.modelbuilder.create_method_from_property(self, mclassdef, false, v.m_signature)
		# Set the signature to keep the same variable
		n_contractdef.n_signature = v.n_signature
		n_contractdef.n_block = n_block
		# Define the location of the new method for corresponding of the annotation location
		n_contractdef.location = v.current_location
		n_contractdef.do_all(v.toolcontext)
	end
end

redef class MExpect

	redef fun is_called(v: ContractsVisitor, mpropdef: MPropDef): Bool
	do
		var main_package = v.mainmodule.mpackage
		var actual_package = mpropdef.mclassdef.mmodule.mpackage
		if main_package != null and actual_package != null then
			var condition_direct_arc = v.toolcontext.modelbuilder.model.mpackage_importation_graph.has_arc(main_package, actual_package)
			return super or main_package == actual_package or condition_direct_arc
		end
		return false
	end

	# Display warning if no contract is defined at introduction `expect`,
	# because if no contract is defined at the introduction the added
	# contracts will not cause any error even if they are not satisfied.
	#
	# Example:
	# ~~~nitish
	# class A
	# 	fun bar [...]
	# 	fun _bar_expect([...])
	# 	do
	# 		[empty contract]
	# 	end
	# end
	#
	# redef class A
	# 	redef fun bar is expect(contract_condition)
	# 	redef fun _bar_expect([...])
	# 	do
	# 		if (contract_condition) then return
	#		super
	# 	end
	# end
	# ~~~~
	#
	redef fun no_intro_contract(v: ContractsVisitor, a: Array[AAnnotation])
	do
		v.toolcontext.warning(a.first.location,"useless_contract","Useless contract: No contract defined at the introduction of the method")
	end

	redef fun create_inherit_nblock(v: ContractsVisitor, n_conditions: Array[AExpr], super_args: Array[AExpr]): ABlockExpr
	do
		var n_block = v.ast_builder.make_block
		for n_condition in n_conditions do
			# Creating the if expression with the new condition
			var if_block = v.ast_builder.make_if(n_condition, n_condition.mtype)
			# Creating and adding return expr to the then case
			if_block.n_then = v.ast_builder.make_return
			if_block.location = n_condition.location
			n_block.add if_block
		end
		n_block.add v.ast_builder.make_super_call(super_args)
		return n_block
	end

	redef fun adapt_method_to_contract(v: ContractsVisitor, mfacet: MFacet, n_mpropdef: AMethPropdef)
	do
		var callsite = v.ast_builder.create_callsite(v.toolcontext.modelbuilder, n_mpropdef, self, true)
		var args = n_mpropdef.n_signature.make_parameter_read(v.ast_builder)
		var n_callexpect = v.ast_builder.make_call(new ASelfExpr, callsite,args)
		# Creation of the new instruction block with the call to expect condition
		var actual_expr = n_mpropdef.n_block
		var new_block = new ABlockExpr
		new_block.n_expr.push v.encapsulated_contract_call(n_mpropdef, [n_callexpect])
		if actual_expr isa ABlockExpr then
			new_block.n_expr.add_all(actual_expr.n_expr)
		else if actual_expr != null then
			new_block.n_expr.push(actual_expr)
		end
		n_mpropdef.n_block = new_block
		mfacet.has_applied_expect = true
	end
end

redef class BottomMContract

	redef fun is_called(v: ContractsVisitor, mpropdef: MPropDef): Bool
	do
		return super or v.mainmodule.mpackage == mpropdef.mclassdef.mmodule.mpackage
	end

	redef fun create_inherit_nblock(v: ContractsVisitor, n_conditions: Array[AExpr], super_args: Array[AExpr]): ABlockExpr
	do
		# Define contract block
		var n_block = v.ast_builder.make_block

		var super_call = v.ast_builder.make_super_call(super_args)

		n_block.add super_call
		for n_condition in n_conditions do
			var tid = new TId.init_tk(n_condition.location)
			tid.text = "{n_condition.location.text}"
			# Creating the assert expression with the new condition
			var n_assert = v.ast_builder.make_assert(tid, n_condition)
			n_assert.location = n_condition.location
			n_block.add n_assert
		end
		return n_block
	end

	# Inject the `result` variable into the `n_block` of the given n_mpropdef`.
	#
	# The purpose of the variable is to capture return values to use it in contracts.
	private fun inject_result(v: ContractsVisitor, n_mpropdef: AMethPropdef, ret_type: MType): Variable
	is
		expect(n_mpropdef.n_signature.n_type != null)
	do
		var actual_block = n_mpropdef.n_block
		# never happen. If it's the case the problem is in the contract facet building
		assert actual_block isa ABlockExpr

		var return_expr = actual_block.n_expr.last.as(AReturnExpr)

		var returned_expr = return_expr.n_expr
		# The return node has no returned expression
		assert returned_expr != null

		var return_var = new Variable("result")
		# Creating a new variable to keep the old return of the method
		var assign_result = v.ast_builder.make_var_assign(return_var, returned_expr)
		# Remove the actual return
		actual_block.n_expr.pop
		# Set the return type
		return_var.declared_type = ret_type
		# Adding the reading expr of result variable to the block
		actual_block.add assign_result
		# Expr to read the result variable
		var read_result = v.ast_builder.make_var_read(return_var, ret_type)
		# Definition of the new return
		return_expr = v.ast_builder.make_return(read_result)
		actual_block.add return_expr
		return return_var
	end
end

redef class MEnsure

	redef fun adapt_specific_msignature(m_signature: MSignature): MSignature
	do
		return m_signature.adapt_to_ensurecondition
	end

	redef fun adapt_specific_nsignature(n_signature: ASignature): ASignature
	do
		return n_signature.adapt_to_ensurecondition
	end

	redef fun adapt_method_to_contract(v: ContractsVisitor, mfacet: MFacet, n_mpropdef: AMethPropdef)
	do
		var callsite = v.ast_builder.create_callsite(v.toolcontext.modelbuilder, n_mpropdef, self, true)
		var n_self = new ASelfExpr
		# argument to call the contract method
		var args = n_mpropdef.n_signature.make_parameter_read(v.ast_builder)

		var actual_block = n_mpropdef.n_block
		# never happen. If it's the case the problem is in the contract facet building
		assert actual_block isa ABlockExpr

		var ret_type = n_mpropdef.mpropdef.mproperty.intro.msignature.return_mtype
		if ret_type != null then
			# Inject the variable result
			var result_var = inject_result(v, n_mpropdef, ret_type)
			# Expr to read the result variable
			var read_result = v.ast_builder.make_var_read(result_var, ret_type)
			var return_expr = actual_block.n_expr.pop
			# Adding the read return to argument
			args.add(read_result)
			var n_call_contract = v.ast_builder.make_call(n_self, callsite, args)
			actual_block.add_all([v.encapsulated_contract_call(n_mpropdef, [n_call_contract]), return_expr])
		else
			# build the call to the contract method
			var n_call_contract = v.ast_builder.make_call(n_self, callsite, args)
			actual_block.add v.encapsulated_contract_call(n_mpropdef, [n_call_contract])
		end
		n_mpropdef.do_all(v.toolcontext)
		mfacet.has_applied_ensure = true
	end
end

redef class MMethod

	# Define contract facet for MMethod in the given mclassdef. The facet represents the entry point with contracts (expect, ensure) of the method.
	# If a contract is given adapt the contract facet.
	#
	# `classdef`: Indicates the class where we want to introduce our facet
	# `exist_contract`: Indicates if it is necessary to define a new facet for the contract. If `exist_contract_facet and exist_contract` it's not necessary to add a facet.
	#
	# Exemple:
	# ~~~nitish
	# from:
	#	classe A
	#		i :Int
	#		fun add_one is ensure(old(i) + 1 == i)
	#	end
	# to:
	#	classe A
	#		fun add_one is ensure(old(i) + 1 == i)
	#
	#		# The contract facet
	#		fun contract_add_one do
	#			add_one
	#			ensure_add_one(old_add_one)
	#		end
	#	end
	# ~~
	private fun define_contract_facet(v: ContractsVisitor, classdef: MClassDef, mcontract: nullable MContract)
	do
		var exist_contract_facet = has_contract_facet
		var contract_facet = build_contract_facet
		# Do nothing the contract and the contract facet already exist
		if mcontract != null and mcontract.is_already_applied(contract_facet) then return

		var n_contract_facet: AMethPropdef
		if not exist_contract_facet then
			# If has no contract facet in intro just create it
			if classdef != intro_mclassdef then
				var n_intro_face = create_facet(v, intro_mclassdef, contract_facet, self)
				n_intro_face.location = self.intro.location
				n_intro_face.do_all(v.toolcontext)
			end
			n_contract_facet = create_facet(v, classdef, contract_facet, self)
		else
			# Check if the contract facet already exist in this context (in this classdef)
			if classdef.mpropdefs_by_property.has_key(contract_facet) then
				# get the definition
				n_contract_facet = v.toolcontext.modelbuilder.mpropdef2node(classdef.mpropdefs_by_property[contract_facet]).as(AMethPropdef)
			else
				# create a new contract facet definition
				n_contract_facet = create_facet(v, classdef, contract_facet, self)
				var block = v.ast_builder.make_block
				# super call to the contract facet
				var args = n_contract_facet.n_signature.make_parameter_read(v.ast_builder)
				var n_super_call = v.ast_builder.make_super_call(args)
				# verification for add a return or not
				if self.intro.msignature.return_mtype != null then
					block.add(v.ast_builder.make_return(n_super_call))
				else
					block.add(n_super_call)
				end
				n_contract_facet.n_block = block
			end
		end
		if mcontract != null then mcontract.adapt_method_to_contract(v, contract_facet, n_contract_facet)

		n_contract_facet.location = v.current_location
		n_contract_facet.do_all(v.toolcontext)
	end

	# Method to create a facet of the method.
	# See `define_contract_facet` for more information about two types of facets.
	#
	# `called` : is the property to call in this facet.
	private fun create_facet(v: ContractsVisitor, classdef: MClassDef, facet: MFacet, called: MMethod): AMethPropdef
	is
		expect( called.is_same_instance(self) or called.is_same_instance(self.mcontract_facet) )
	do
		# Defines the contract facet is an init or not
		# it is necessary to use the contracts with in a constructor
		facet.is_init = is_init
		var n_contractdef = v.toolcontext.modelbuilder.create_method_from_property(facet, classdef, false, self.intro.msignature)
		# FIXME set the location because the callsite creation need the node location
		n_contractdef.location = v.current_location
		n_contractdef.validate

		var block = v.ast_builder.make_block

		# Arguments to call the `called` property
		var args: Array[AExpr]
		args = n_contractdef.n_signature.make_parameter_read(v.ast_builder)

		var callsite = v.ast_builder.create_callsite(v.toolcontext.modelbuilder, n_contractdef, called, true)
		var n_call = v.ast_builder.make_call(new ASelfExpr, callsite, args)

		if self.intro.msignature.return_mtype == null then
			block.add(n_call)
		else
			block.add(v.ast_builder.make_return(n_call))
		end

		n_contractdef.n_block = block
		n_contractdef.do_all(v.toolcontext)
		return n_contractdef
	end
end

redef class MMethodDef

	# Entry point to build contract (define the contract facet and define the contract method verification)
	private fun construct_contract(v: ContractsVisitor, n_signature: ASignature, n_annotations: Array[AAnnotation], mcontract: MContract, exist_contract: Bool)
	do
		v.define_signature(mcontract, n_signature, mproperty.intro.msignature)
		if not exist_contract and not is_intro then no_intro_contract(v, mcontract, n_annotations)
		v.build_contract(n_annotations, mcontract, mclassdef)
		# Check if the contract must be called to know if it's needed to construct the facet
		if mcontract.is_called(v, self) then mproperty.define_contract_facet(v, mclassdef, mcontract)
	end

	# Create a contract on the introduction classdef of the property.
	# Display an warning message if necessary
	private fun no_intro_contract(v: ContractsVisitor, mcontract: MContract, n_annotations: Array[AAnnotation])
	do
		v.toolcontext.modelbuilder.create_method_from_property(mcontract, mcontract.intro_mclassdef, false, v.m_signature)
		mcontract.no_intro_contract(v, n_annotations)
	end

	# Apply the `no_contract` annotation to the contract. This method removes the inheritance by adding an empty contract method.
	# Display a warning if the annotation is not needed
	private fun no_contract_apply(v: ContractsVisitor, n_signature: ASignature)
	do
		var mensure = mproperty.mensure
		var mexpect = mproperty.mexpect
		if mensure == null and mexpect == null then
			v.toolcontext.warning(location, "useless_nocontract", "Useless `no_contract`, no contract was declared for `{name}`. Remove the `no_contract`")
		end
		if mensure != null then
			# Add an empty ensure method to replace the actual definition
			v.toolcontext.modelbuilder.create_method_from_property(mensure, mclassdef, false, mensure.intro.msignature)
		end
		if mexpect != null then
			# Add an empty expect method to replace the actual definition
			v.toolcontext.modelbuilder.create_method_from_property(mexpect, mclassdef, false, mexpect.intro.msignature)
		end
	end
end

redef class APropdef
	redef fun check_callsite(v)
	do
		v.visited_propdef = self
	end
end

redef class AMethPropdef

	# Entry point to create a contract (verification of inheritance, or new contract).
	redef fun create_contracts(v)
	do
		v.ast_builder.check_mmodule(mpropdef.mclassdef.mmodule)
		v.current_location = self.location
		v.is_intro_contract = mpropdef.is_intro
		check_annotation(v)
		if not mpropdef.is_intro then check_redef(v)
	end

	# Verification of the annotation to know if it is a contract annotation.
	# If this is the case, we built the appropriate contract.
	private fun check_annotation(v: ContractsVisitor)
	do
		var annotations_expect = get_annotations("expect")
		var annotations_ensure = get_annotations("ensure")
		var annotation_no_contract = get_annotations("no_contract")

		if (not annotations_expect.is_empty or not annotations_ensure.is_empty) and not annotation_no_contract.is_empty then
			v.toolcontext.error(location, "The new contract definition is not correct when using `no_contract`. Remove the contract definition or the `no_contract`")
			return
		end

		var nsignature = n_signature or else new ASignature

		if not annotation_no_contract.is_empty then
			mpropdef.no_contract_apply(v, nsignature.clone)
			return
		end

		if not annotations_expect.is_empty then
			var exist_contract = mpropdef.mproperty.has_expect
			mpropdef.construct_contract(v, nsignature.clone, annotations_expect, mpropdef.mproperty.build_expect, exist_contract)
		end

		if not annotations_ensure.is_empty then
			var exist_contract = mpropdef.mproperty.has_ensure
			mpropdef.construct_contract(v, nsignature.clone, annotations_ensure, mpropdef.mproperty.build_ensure, exist_contract)
		end
	end

	# Verification if the method have an inherited contract to apply it.
	private fun check_redef(v: ContractsVisitor)
	do
		var mexpect = mpropdef.mproperty.mexpect
		var mensure = mpropdef.mproperty.mensure
		var mcontract_facet = mpropdef.mproperty.mcontract_facet

		if mexpect != null then
			if mcontract_facet != null and mcontract_facet.has_applied_expect then return
			if mexpect.is_called(v, mpropdef.as(not null)) then mpropdef.mproperty.define_contract_facet(v, mpropdef.mclassdef, mexpect)
		end
		if mensure != null then
			if mcontract_facet != null and mcontract_facet.has_applied_ensure then return
			if mensure.is_called(v, mpropdef.as(not null)) then mpropdef.mproperty.define_contract_facet(v, mpropdef.mclassdef, mensure)
		end
	end
end

redef class MSignature

	# Adapt signature for an contract
	#
	# The returned `MSignature` is the copy of `self` without return type.
	private fun adapt_to_contract: MSignature do return new MSignature(mparameters.to_a, null)

	# Adapt signature for a ensure contract
	#
	# The returned `MSignature` is the copy of `self` without return type.
	# The return type is replaced by a new parameter `result`
	private fun adapt_to_ensurecondition: MSignature
	do
		var rtype = return_mtype
		var msignature = adapt_to_contract
		if rtype != null then
			msignature.mparameters.add(new MParameter("result", rtype, false))
		end
		return msignature
	end

	# The returned `MSignature` is the exact copy of `self`.
	private fun clone: MSignature do return new MSignature(mparameters.to_a, return_mtype)
end

redef class ASignature

	# Create an array of AVarExpr representing the read of every parameters
	private fun make_parameter_read(ast_builder: ASTBuilder): Array[AVarExpr]
	do
		var args = new Array[AVarExpr]
		for n_param in self.n_params do
			var mtype = n_param.variable.declared_type
			var variable = n_param.variable
			if variable != null and mtype != null then
				args.push ast_builder.make_var_read(variable, mtype)
			end
		end
		return args
	end

	# Create a new ASignature adapted for contract
	#
	# The returned `ASignature` is the copy of `self` without return type.
	private fun adapt_to_contract: ASignature
	do
		var adapt_nsignature = self.clone
		if adapt_nsignature.n_type != null then adapt_nsignature.n_type.detach
		return adapt_nsignature
	end

	# Create a new ASignature adapted for ensure
	#
	# The returned `ASignature` is the copy of `self` without return type.
	# The return type is replaced by a new parameter `result`
	private fun adapt_to_ensurecondition: ASignature do
		var nsignature = adapt_to_contract
		if ret_type != null then
			var variable = new Variable("result")
			variable.declared_type = ret_type
			nsignature.n_params.add new AParam.make(variable, ret_type.create_ast_representation)
		end
		return nsignature
	end
end

redef class ASendExpr
	redef fun check_callsite(v)
	do
		var actual_callsite = callsite
		if actual_callsite != null then
			callsite = v.drive_callsite_to_contract(actual_callsite)
			# Set the signature mapping with the old value, this avoids having to re-check the callsite.
			callsite.signaturemap = actual_callsite.signaturemap
		end
	end
end

redef class ANewExpr
	redef fun check_callsite(v)
	do
		var actual_callsite = callsite
		if actual_callsite != null then
			callsite = v.drive_callsite_to_contract(actual_callsite)
			# Set the signature mapping with the old value, this avoids having to re-check the callsite
			callsite.signaturemap = actual_callsite.signaturemap
		end
	end
end
src/contracts.nit:15,1--855,3