Skip to main content

module analysis::m3::Core


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


import analysis::m3::Core;


import Message;
import Set;
import IO;
import util::FileSystem;
import analysis::graphs::Graph;
import Node;
import Map;
import List;
import Relation;
extend analysis::m3::TypeSymbol;


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 a 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

m3 model constructor

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.)
set[loc] implicitDeclarations = {}, // some languages provide builtin implicit declarations, e.g. |java+class:///java/lang/Object| may not have a source location but still exist.
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 inner declaration (to) is logically contained in what outer declaration (`from`) (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)

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

Create an empty m3 term with empty annotations

M3 emptyM3(loc id)

function composeM3

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

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

function diffM3

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

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

function modifyM3

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

function isEmpty

bool isEmpty(M3 model)

function relToFileSystem

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

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

function files

set[loc] files(M3 model)

function containmentToFileSystem

transform the containment relation to a recursive tree model

set[FileSystem] containmentToFileSystem(M3 model)


  • Transforming the containment relation to a tree model allows further analysis using operators such as visit and descendant matching (/)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)

function testM3ModelConsistency

bool testM3ModelConsistency(M3 m)