Skip to main content

module analysis::m3::Core


import analysis::m3::Core;


M3 common source code model represent facts extracted from source code for use in downstream metrics or other analyses.


The M3 Core defines basic concepts such as:

  • qualified names: we use Locations to model qualified names for each programming language
  • containment: which artifacts are contained in which other artifacts
  • declarations: where artifacts are defined
  • uses: where declared artifacts are used
  • types: which artifacts has which types

From this Core is supposed to be extended with features specific for a programming language. See for example Java M3.


  • Qualified names in the shape of Location are a uniform and generic way of identifying source code artifacts, that can be extended across languages, projects, and versions.
  • M3 helps standardizing the shape of facts we extract from source code for all different languages, limiting the element of surprise.
  • When we use M3 for many languages, common IDE features are made reusable (such as clicking from an extracted fact to the code that generated it).
  • Some downstream analyses may be reusable between different languages if they all map to M3.


  • Even though different languages may map to the same M3 model, this does not mean that the semantics is the same. Downstream metrics or other analysis tools should still take semantic differences between programming languages into account.

data M3

data M3 (
rel[loc name, loc src] declarations = {}, // maps declarations to where they are declared. contains any kind of data or type or code declaration (classes, fields, methods, variables, etc. etc.)
rel[loc name, TypeSymbol typ] types = {}, // assigns types to declared source code artifacts
rel[loc src, loc name] uses = {}, // maps source locations of usages to the respective declarations
rel[loc from, loc to] containment = {}, // what is logically contained in what else (not necessarily physically, but usually also)
list[Message] messages = [], // error messages and warnings produced while constructing a single m3 model
rel[str simpleName, loc qualifiedName] names = {}, // convenience mapping from logical names to end-user readable (GUI) names, and vice versa
rel[loc definition, loc comments] documentation = {}, // comments and javadoc attached to declared things
rel[loc definition, Modifier modifier] modifiers = {} // modifiers associated with declared things
= m3(
loc id)


m3 model constructor


This constructor holds all information to an m3 model. It is identified by the id field, which should be a unique name for the project or file that the m3 model was constructor for.

Attached to this m3 model will be annotations with the specific information.

data Language

data Language (str version = "") 
= generic()

function emptyM3

M3 emptyM3(loc id)

Create an empty m3 term with empty annotations

function composeM3

M3 composeM3(loc id, set[M3] models)

Generic function to compose the annotations of a set of M3s.

function diffM3

M3 diffM3(loc id, list[M3] models)

Generic function to apply a difference over the annotations of a list of M3s.

function modifyM3

M3 modifyM3(loc id, list[M3] models, value (&T,&T) fun)

function isEmpty

bool isEmpty(M3 model)

function relToFileSystem

set[FileSystem] relToFileSystem(rel[loc parent, loc child] r)


constructs a recursive FileSystem from a binary [Location] relation.


function files

set[loc] files(M3 model)

function containmentToFileSystem

set[FileSystem] containmentToFileSystem(M3 model)


transform the containment relation to a recursive tree model


  • Transforming the containment relation to a tree model allows further analysis using operators such as Visit and Descendant which is sometimes more convenient.


  • Do not forget that the relational operators such as [TransitiveClosure], [Comprehension] and [Composition] may be just as effective and perhaps more efficient, as applied directly on the containment relation.

function checkM3

list[Message] checkM3(M3 model)