model-driven software development - static analysis & error checking

59
Static Analysis & Error Checking Course IN4308 Master Computer Science Delft University of Technology Eelco Visser http://eelcovisser.org Lecture 9

Upload: eelco-visser

Post on 26-May-2015

1.702 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Model-Driven Software Development - Static Analysis & Error Checking

Static Analysis & Error Checking

Course IN4308Master Computer Science

Delft University of Technology

Eelco Visserhttp://eelcovisser.org

Lecture 9

Page 2: Model-Driven Software Development - Static Analysis & Error Checking

Coming up

Lecture 8: Context-sensitive transformation

★ design 2

★ transformation with dynamic rewrite rules

Lecture 9: Static analysis & error checking

★ name resolution, reference resolution

★ type analysis

Lecture 10: Code generation

★ string templates, code generation by model transformation

★ concrete object syntax

Lecture 11: Code generation strategies

★ customization of generated code

Page 3: Model-Driven Software Development - Static Analysis & Error Checking

Consistency Checking

Page 4: Model-Driven Software Development - Static Analysis & Error Checking

Consistency Checking

Syntax definition

★ what are well-formed sentences?

Static analysis

★ not all ‘well-formedness’ properties are context-free

★ consistency of compositions

★ consistency of expressions wrt declarations

Error reporting

★ indicate errors in editor

★ use sensible error message

Page 5: Model-Driven Software Development - Static Analysis & Error Checking
Page 6: Model-Driven Software Development - Static Analysis & Error Checking
Page 7: Model-Driven Software Development - Static Analysis & Error Checking

Consistency Checking: Ingredients

Editor Interface

★ collecting and displaying errors, warnings

Error checking

★ checking static constraints and reporting errors

Type analysis

★ computing types of expressions

Name resolution

★ disambiguation of names

Reference resolving

★ linking identifiers to declarations

Page 8: Model-Driven Software Development - Static Analysis & Error Checking

Consistency Checking: Generic Approach

Rename

★ make identifiers unique

Map

★ map identifiers to declarations

Project

★ compute properties of declarations, expressions

Check

★ check constraints

Page 9: Model-Driven Software Development - Static Analysis & Error Checking

Editor Interface

module nwl-Builders

imports nwl-Builders.generated

builders

provider: include/nwl.ctree

observer: editor-analyze

module static-analysis

imports include/nwlimports entitiesimports utils

rules // static analysis

editor-analyze: (ast, path, fullpath) -> (errors, warnings, notes) with ...

editor/nwl-Builders.esvtrans/static-analysis.str

Page 10: Model-Driven Software Development - Static Analysis & Error Checking

Editor Interface

editor-analyze: (ast, path, fullpath) -> (errors, warnings, notes) with errors := <collect-all(check, conc)> ast; warnings := <collect-all(constraint-warning, conc)> ast; notes := <collect-all(constraint-note, conc)> ast

Page 11: Model-Driven Software Development - Static Analysis & Error Checking

Error Checking Rules

check : context -> (target, error) where assumption where require(constraint)

require(s) = not(s)

– Context: identifying points in the code to check– Assumptions: only report an error if certain assumptions hold (validating the context and avoiding spurious errors)– Constraints: checking for constraints at the context– Formulating an error message– Attribution of the error to a particular character range in the source text (usually, only part of the context

Page 12: Model-Driven Software Development - Static Analysis & Error Checking

Error Checking: Binary Operators

check : e@BinOp(e1, op, e2) -> (e, $[operator [op] not defined for [<pp>t1] and [<pp>t2]]) where t1 := <type-of> e1 where t2 := <type-of> e2 where require(<type-of> e)

Page 13: Model-Driven Software Development - Static Analysis & Error Checking

Pretty-Printing with String Interpolation

pp : Entity(x, prop*) -> $[entity [x] { [<map(pp)>prop*] }] pp : Property(x,t) -> $[[x] : [<pp>t] ] pp : SimpleType(x) -> x pp : SetType(t) -> $[Set<[<pp> t]>] pp : [] -> $[] pp : [t] -> <pp>t pp : [t1,t2|ts] -> $[[<pp>t1],[<pp>[t2|ts]]]

Page 14: Model-Driven Software Development - Static Analysis & Error Checking

Origin Trackingcheck : e@BinOp(e1, op, e2) -> (e, $[operator [op] not defined for [<pp>t1] and [<pp>t2]]) where ...

Assign( Var("x"), Plus(IntLit("2"), Times(IntLit("3"), StringLit("4"))))

Assign( Var("x"), BinOp( IntLit("2") , "+" , BinOp(IntLit("3"), "+", StringLit("4")) ))

Page 15: Model-Driven Software Development - Static Analysis & Error Checking

Error Checking: Control-Flow Statements

check : While(e, b) -> (e, $[Expression should have type Bool]) where t := <type-of> e where require(<eq>(t, SimpleType("Bool"))) check : If(e, b1, b2) -> (e, $[Expression should have type Bool]) where t := <type-of> e where require(<eq>(t,SimpleType("Bool")))

check : For(x, t, e, elem*) -> (e, $[[<pp>SetType(t)] expected]) where t2 := <type-of> e where require(<eq>(t2,SetType(t)))

check rules follow the same pattern: type analysis + local consistency check

Page 16: Model-Driven Software Development - Static Analysis & Error Checking

Type Analysis

type-of : e -> t

compute type of expression

Page 17: Model-Driven Software Development - Static Analysis & Error Checking

Type Analysis: Literals

type-of : StringLit(x) -> SimpleType("String") type-of : IntLit(x) -> SimpleType("Int")

Page 18: Model-Driven Software Development - Static Analysis & Error Checking

Type Analysis: Binary Operators

type-of : BinOp(e1, op, e2) -> t where t := <function-type>(op, [<type-of>e1, <type-of>e2])

function-type : ("+", [SimpleType("String"), SimpleType("String")]) -> SimpleType("String") function-type : ("+", [SimpleType("Int"), SimpleType("Int")]) -> SimpleType("Int")

function-type : ("-", [SimpleType("Int"), SimpleType("Int")]) -> SimpleType("Int")

Page 19: Model-Driven Software Development - Static Analysis & Error Checking

BinOp( IntLit("2"), "+", BinOp( IntLit("3"), "+", IntLit("4") ))

Type Analysis

Page 20: Model-Driven Software Development - Static Analysis & Error Checking

BinOp( IntLit("2"), "+", BinOp( IntLit("3"), "+", IntLit("4") ))

SimpleType("Int")type-of

SimpleType("Int")type-of

Type Analysis

Page 21: Model-Driven Software Development - Static Analysis & Error Checking

BinOp( IntLit("2"), "+", BinOp( IntLit("3"), "+", IntLit("4") ))

SimpleType("Int")type-of

SimpleType("Int")type-of

Type Analysis

Page 22: Model-Driven Software Development - Static Analysis & Error Checking

BinOp( IntLit("2"), "+", BinOp( IntLit("3"), "+", IntLit("4") ))

SimpleType("Int")type-of

Type Analysis

Page 23: Model-Driven Software Development - Static Analysis & Error Checking

Type Analysis: What is Type of Variable?

define page root(x : Int) { action exptest() { for(y : Int in {1,2,x}) { x := x + y; } } } type-of :

Var(x) -> t where t := ???

Assign( Var("x"), BinOp(Var("x"), "+", Var("y")))

type of variable not part of variable use

Page 24: Model-Driven Software Development - Static Analysis & Error Checking

Variables: Map

declare-all = alltd(declare)

declare : Param(x, t) -> Param(x, t) with rules( TypeOf : x -> t )

type-of : Var(x) -> t where t := <TypeOf> x

Page 25: Model-Driven Software Development - Static Analysis & Error Checking

Scope

define page root(x : Int) { action exptest() { for(x : Int in {1,2,x}) { print(x); } }}

multiple occurrences of same identifier corresponding to different declarations

Page 26: Model-Driven Software Development - Static Analysis & Error Checking

Variables: Map + Renamerename-all = alltd(rename)

rename : Param(x, t) -> Param(y, t) with y := <rename-var>(x, t)

rename-var : (x, t) -> y with y := x{<new>}; rules( TypeOf : y -> t RenameId : x -> y )

rename : Var(x) -> Var(y) where y := <RenameId> x type-of : Var(x) -> t where t := <TypeOf> x

unique annotation

rename occurrences

map variable to type

Page 27: Model-Driven Software Development - Static Analysis & Error Checking

Term Annotations

t{t1,...,tn}

add additional information to term without affecting signature

Page 28: Model-Driven Software Development - Static Analysis & Error Checking

Variables: Check

check : e@Var(x) -> (e, $[Variable '[x]' not declared]) where require(<type-of>e)

Page 29: Model-Driven Software Development - Static Analysis & Error Checking

Variable Binding Constructs

rename : For(x, t, e1, stat1*) -> For(y, t, e2, stat2*) with e2 := <rename-all> e1 with {| RenameId : y := <rename-var>(x, t) ; stat2* := <rename-all> stat1* |}

For defines local variable x in body stat1*’

Page 30: Model-Driven Software Development - Static Analysis & Error Checking

is-lvalue = ?Var(_) <+ ?PropertyAccess(_, _)

check : Assign(e1, e2) -> (e1, $[Left-hand side of assignment should be variable or property access]) where require(<is-lvalue> e1)

check : Assign(e1, e2) -> (<id>, $[Type of lhs ('[<pp>t1]') does not match type of rhs ('[<pp>t2]')]) where t1 := <type-of>e1 where t2 := <type-of>e2 where require(<eq>(t1, t2))

Assignment

Page 31: Model-Driven Software Development - Static Analysis & Error Checking

Editor Interface with Analysis

editor-analyze: (ast, path, fullpath) -> (errors, warnings, notes) with ast2 := <analyze> ast; errors := <collect-all(check, conc)> ast2; warnings := <collect-all(constraint-warning, conc)> ast2; notes := <collect-all(constraint-note, conc)> ast2

analyze = rename-all

Page 32: Model-Driven Software Development - Static Analysis & Error Checking

Rename, Map, Project, Check

Rename

★ make local variables unique

Map

★ variables to their type

Project

★ compute type of expressions

Check

★ check constraints using types

Page 33: Model-Driven Software Development - Static Analysis & Error Checking

Data Model Consistency

Page 34: Model-Driven Software Development - Static Analysis & Error Checking

Consistency of Data Model Declarations

entity Blog { url : String (id) name : String (name) posts : Set<Post> author : User }

entity Post { url : String (id) title : String (name) text : WikiText blog : Blog (inverse:posts) author : User blog : Blog version : Int}

Page 35: Model-Driven Software Development - Static Analysis & Error Checking

Consistency Constraints for Data Models

Unique declarations

★ entity names unique in model

★ property names unique in entity

Valid types

★ type is either primitive types (e.g. String) or declared entity type

Inverse properties

★ should refer to existing entity with existing property

Page 36: Model-Driven Software Development - Static Analysis & Error Checking

Rename; Map; Project; Check

Rename

★ not needed: top-level declarations have global scope

Map

★ map identifier to AST of declaration

Project

★ lookup information in declaration

Check

★ check consistency using map & project

Page 37: Model-Driven Software Development - Static Analysis & Error Checking

declare-def: ent@Entity(x, prop*) -> Entity(x, prop*) with rules( EntityDeclaration : x -> ent )

declaration-of : SimpleType(x) -> <EntityDeclaration> x

Entity Declarations: Map & Project

carrier-type = try(?SetType(<id>))

is-entity-type = where(SimpleType(EntityDeclaration))

is-simple-type = is-primitive-type <+ is-entity-type

name-of : Entity(x, prop*) -> x type-of : Entity(x, prop*) -> SimpleType(x)

Map

Project

Page 38: Model-Driven Software Development - Static Analysis & Error Checking

Entity Declarations: Check

check : ent@Entity(x, prop*) -> (x, $[Entity '[x]' defined more than once]) where require(<EntityDeclaration> x => ent) check : t@SimpleType(x) -> (x, $[Type '[x]' is not defined]) where require(<is-simple-type>t) check : t@SetType(type) -> (t, $[Set should have entity type as argument]) where <is-simple-type> type where require(<is-entity-type> type)

Page 39: Model-Driven Software Development - Static Analysis & Error Checking

Properties: Project

lookup-property(|x) = lookup-property(?Property(x,_,_)) lookup-property(s) : Entity(x, prop*) -> <fetch-elem(s)> prop* lookup-property(s) : SimpleType(x) -> <declaration-of; lookup-property(s)> type-of : Property(_, type, _) -> type inverse : Property(_, _, anno*) -> <fetch-elem(?Inverse(_))> anno*

Page 40: Model-Driven Software Development - Static Analysis & Error Checking

Properties: Check

check: ent@Entity(x, prop*) -> errors where errors := <filter(check-property(|ent))> prop* where require(<not(?[])> errors)

check-property(|ent) : Property(name, type, annos) -> (name, $[Property '[name]' defined more than once]) where require(<type-of><lookup-property(|name)>ent => type)

Page 41: Model-Driven Software Development - Static Analysis & Error Checking

Inverse Property: Check

check-property(|ent) : prop@Property(f, t, annos) -> (g, $[Inverse relation requires entity type]) where Inverse(g) := <inverse>prop where tc := <carrier-type> t where <is-simple-type> tc where require(<is-entity-type> tc)

Page 42: Model-Driven Software Development - Static Analysis & Error Checking

Inverse Property: Check

check: ent@Entity(x, prop*) -> errors where errors := <filter(check-property(|ent)); not(?[])> prop* check-property(|ent) : Property(f, t, annos) -> (g, $[Inverse relation requires entity type]) where Inverse(g) := <inverse> where tc := <carrier-type> t where <is-simple-type> tc // non-existing type already produces error message where require(<is-entity-type> tc) check-property(|ent1) : Property(f, t, annos) -> (g, $[Entity '[<pp>tc]' has no property '[g]']) where Inverse(g) := <inverse> where tc := <carrier-type> t where <is-entity-type> tc where require(<lookup-property(|g)> tc) check-property(|ent) : Property(f, t, anno*) -> (g, $[Type of '[<pp>t1].[g]' should be [<pp>t3] or [<pp>SetType(t3)]]) where Inverse(g) := <inverse> where t1 := <carrier-type> t where t2 := <lookup-property(|g); type-of; carrier-type> t1 where t3 := <type-of>ent where require(<eq>(t2, t3))

multiple check rules necessary to check different cases

Page 43: Model-Driven Software Development - Static Analysis & Error Checking

Property References

type-of : PropertyAccess(e, f) -> <type-of; lookup-property(|f); type-of> e

check : e1@PropertyAccess(e2, f) -> (f, $[[<pp>t] has no property '[f]]) where t := <type-of> e2 where require(<type-of>e1)

Page 44: Model-Driven Software Development - Static Analysis & Error Checking

Template Consistency

Page 45: Model-Driven Software Development - Static Analysis & Error Checking

Template definitions

★ should be unique

Template references

★ to existing definition

★ consistent with parameter declarations

Template Consistencydefine page editpost(p : Post) { action save() { p.version := p.version + 1; return post(p); } header{output(p.title)} form{ input(p.url) input(p.title) input(p.text) submit save() { "Save" } }}

Page 46: Model-Driven Software Development - Static Analysis & Error Checking

Template ASTdefine page editpost(p : Post) { action save() { p.version := p.version + 1; return post(p); } header{output(p.title)} form{ input(p.url) input(p.title) input(p.text) submit save() { "Save" } }}

TemplateDef( [Page()], "editpost", [Param("p", SimpleType("Post"))], [ Action( "save" , [] , [ Assign( PropertyAccess(Var("p"), "version") , Plus(PropertyAccess(Var("p"), "version"), IntLit("1")) ) , ReturnPage(PageRef("post", [Var("p")])) ] ) , CallElems( "header" , [CallArgs("output", [PropertyAccess(Var("p"), "title")])] ) , CallElems( "form" , [ CallArgs("input", [PropertyAccess(Var("p"), "url")]) , CallArgs("input", [PropertyAccess(Var("p"), "title")]) , CallArgs("input", [PropertyAccess(Var("p"), "text")]) , Submit("save", [], [String(""Save"")]) ] ) ])

Page 47: Model-Driven Software Development - Static Analysis & Error Checking

Template Definitions: Map + Rename

declare-def : def@TemplateDef(mod*, x, param*, elem*) -> def with sig := <signature-of> def; rules( Template : x -> def Template : sig -> def )

rename : TemplateDef(mod*, x, param1*, elem1*) -> <declare-def> TemplateDef(mod*, x, param2*, elem3*) with {| RenameId, RenameAction : param2* := <rename-all> param1* ; elem2* := <alltd(rename-action)> elem1* ; elem3* := <rename-all> elem2* |}

rename local variables in template definition

Page 48: Model-Driven Software Development - Static Analysis & Error Checking

Template Definitions: Projectis-page-def = ?TemplateDef([Page()],_,_,_)

param-types = is-list; map(?Param(_,<id>)) param-types : TemplateDef(mod*, x, param*, elem*) -> <param-types> param* signature-of : TemplateDef(mod*, x, param*, elem*) -> (x, <param-types>param*) declaration-of : TemplateDef(mod*, x, param*, elem*) -> <signature-of; Template>

declaration-of : Navigate(ref, elems) -> <declaration-of> ref declaration-of : PageRef(x, e*) -> <Template> x call-of : PageRef(x, e*) -> (x, e*)

Page 49: Model-Driven Software Development - Static Analysis & Error Checking

Template Definitions: Check Uniqueness

check : def@TemplateDef(mod*, x, param*, elem*) -> (x, $[Multiple definitions for page '[x]']) where <is-page-def> def where require(<Template> x => def) check : def@TemplateDef(mod*, x, param*, elem*) -> (x, $[Multiple definitions for template with signature [sig]]) where not(is-page-def) where require(<declaration-of> def => def) where sig := <signature-of;pp-sig> def

Page 50: Model-Driven Software Development - Static Analysis & Error Checking

Checking Template/Page/Function Calls

List of expressions consistent with list of types

★ zip

Multiple possible error causes

★ call of non-existing definition

★ parameter arity mismatch

★ argument type mismatch

Argument checking reusable

Page 51: Model-Driven Software Development - Static Analysis & Error Checking

Templates: Check Page References

check : PageRef(x, e*) -> (x, $[Navigation to non-existing page]) where require(declaration-of) check : PageRef(x, e*) -> [(x, $[Navigation to template '[x]' (not a page)])] where def := <declaration-of> where require(<is-page-def> def) check : PageRef(x, e*) -> <check-args>

Page 52: Model-Driven Software Development - Static Analysis & Error Checking

Template Call: Project

signature-of : Call(x, e*, elem*) -> (x, <map(type-of)> e*)

call-of : Call(x, e*, elem*) -> (x, e*)

declaration-of : Call(x, e*, elem*) -> <signature-of; Template>

is-primitive-template = ?"input" <+ ?"output" <+ ?"form"

Page 53: Model-Driven Software Development - Static Analysis & Error Checking

Templates: Check Template Calls

check : Call(x, e*, elem*) -> (x, $[Template '[x]' is not defined]) where not(<is-primitive-template> x) where require(<Template> x)

check : Call(x, e*, elem*) -> (x, $[No definition for template with signature '[x]([<map(type-of);pp> e*])']) where not(<is-primitive-template> x) where <Template> x where require(declaration-of) constraint-warning : Call(x, e*, elem*) -> [(x, $[Page definition is used as template])] where def := <declaration-of> where require(not(<is-page-def> def)) check : Call(x, e*, elem*) -> <check-args>

Page 54: Model-Driven Software Development - Static Analysis & Error Checking

Checking Call Arguments

check-args = !(<call-of>, <declaration-of>); (check-arg-types <+ check-args-arity)

check-arg-types : ((f, e*), def) -> errors where errors := <zip; filter(check-arg); not(?[])> (e*, <param-types> def) check-arg : (e, t) -> (e, $[Argument of type '[<pp>t]' expected (not of type '[<pp>t2]')]) where t2 := <type-of> e where require(<eq>(t, t2)) check-args-arity : ((f, e*), def) -> [(f, $['[f]' expects [<int-to-string>l] arguments; [<int-to-string>k] provided])] with k := <length>e* with l := <param-types; length> def where require(<eq>(k, l))

Page 55: Model-Driven Software Development - Static Analysis & Error Checking

Reference Resolution

Page 56: Model-Driven Software Development - Static Analysis & Error Checking

Reference Resolution

Aiding program navigation

★ Hover-click on identifier to jump to declaration

Reuse name resolution infrastructure

Page 57: Model-Driven Software Development - Static Analysis & Error Checking

editor-resolve: (source, position, ast, path, fullpath) -> target where target := <compute-target> source

Reference Resolution

module nwl-References

imports nwl-References.generated

references

reference _ : editor-resolve

Page 58: Model-Driven Software Development - Static Analysis & Error Checking

editor-resolve: (SimpleType(type), position, ast, path, fullpath) -> target where Entity(target,_) := <EntityDeclaration> type editor-resolve: (ref@PageRef(x,e*), position, ast, path, fullpath) -> target where TemplateDef(_,target,_,_) := <declaration-of> ref

From Use to Declaration

Page 59: Model-Driven Software Development - Static Analysis & Error Checking

Schedule

Case 3

★ Syntax definition & term rewriting

★ Deadline: May 4

Design 2

★ Make a proposal (can be submitted separately)

★ Deadline: May 5

Lab this week

★ Finish Case 3

★ Syntax for Design 2

Next

★ Lecture 10: code generation