Changes between Initial Version and Version 1 of SemanticQuery/Highlights


Ignore:
Timestamp:
Sep 17, 2014, 4:45:50 PM (10 years ago)
Author:
manualwiki
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • SemanticQuery/Highlights

    v1 v1  
     1= Retrieving semantic information about specs and types = 
     2 
     3Erlang is dynamically typed, but it provides notations for types in the Erlang syntax. Although these do not affect the operation of the compiled code, it is generally a good practice to utilize them for type checking and other purposes, like doc generation. Our tool analyzes and stores "-type", "-opaque", "-export_type", "-spec" attributes and the types of record fields. These information can be conveniently accessed/used and comprehended via semantic queries. 
     4 
     5Below is the brief description of available selectors in sq regarding type informations. For other available selectors, please visit our dedicated wiki page! 
     6 
     7=== Initial selectors: === 
     8{{{ 
     9@spec →         Spec 
     10        The spec at the given position. 
     11@type → Type 
     12        The type expression at the given position. 
     13}}} 
     14 
     15=== File selectors: === 
     16{{{ 
     17File.[spec, specs] →    Spec 
     18        Specs defined in the file. 
     19File.typerefs →         Type 
     20        Types used in the file. 
     21File.[typedef, typedefs, types, type] →         Type 
     22        Types defined in the file. 
     23}}} 
     24 
     25=== Function selectors: === 
     26{{{ 
     27Fun.spec →      Spec 
     28        Specification of the given function. 
     29Fun.[returntype, returntypes, rettype] →        Type 
     30        Possible return types of the function according to the spec. 
     31Fun.[arguments, argument, args, arg, parameters, parameter, params, param] →    FunParam 
     32        The arguments of the given function. 
     33}}} 
     34 
     35=== Function clause selectors: === 
     36{{{ 
     37FunClause.spec →        Spec 
     38        The specification of the function defining the given function clause. 
     39FunClause.[returntype, returntypes, rettype] →  Type 
     40        Possible return types of the clause according to the spec. 
     41FunClause.[arguments, argument, args, arg, parameters, parameter, params, param] →      FunParam 
     42        The arguments of the given function clause. 
     43}}} 
     44 
     45=== Spec selectors: === 
     46{{{ 
     47Spec.['fun', func, function]→   Fun 
     48        The function that has this spec. 
     49Spec.[file, deffile] →  File 
     50        The file in which the spec was defined. 
     51Spec.[modref, refmod] →         File 
     52        The file to which the spec pertains. 
     53Spec.[returntype, returntypes] →        Type 
     54        Possible return types of the spec. 
     55Spec.[arguments, args, params, parameters, param, arg, argument] →      Specparam 
     56        Arguments of the spec. 
     57Spec.[guardtype, guardtypes] →  Type 
     58        Types of the spec-guards. 
     59Spec.returntext →       string 
     60        String representation of the specs return values. 
     61Spec.guardtext →        string 
     62        String representation of the specs guards. 
     63Spec.[name, funname] →  atom 
     64        Name of the function spec. 
     65Spec.arity →    int 
     66        Arity of the spec. 
     67Spec.text →     string 
     68        String representation of the spec. 
     69}}} 
     70 
     71=== Specparam selectors: === 
     72{{{ 
     73Specparam.type →        Type 
     74        The type of the spec-parameter. 
     75Specparam.name →        string 
     76        Name of the spec-parameter. 
     77Specparam.index →       int 
     78        Index of the spec-parameter. 
     79Specparam.text →        string 
     80        String representation of the spec-parameter. 
     81}}} 
     82 
     83=== Type selectors: === 
     84{{{ 
     85Type.file →     File 
     86        The defining file of the type. 
     87Type.[params, param, typeparams, typeparam, par, pars] →        Type 
     88        The parameters of the type. 
     89Type.[subtype, subtypes, sub] →         Type 
     90        Subtypes of the given type. 
     91Type.[specref, specrefs, refspec, refspecs] →   Spec 
     92        Specs using this type. 
     93Type.[exported, remote_type] →  bool 
     94        Returns whether the type is exported. 
     95Type.name →     atom 
     96        The name of the type. 
     97Type.[arity, number_of_parameters] →    int 
     98        The arity of the type. 
     99Type.[isopaque, opaque, is_opaque] →    bool 
     100        Returns whether the type is opaque. 
     101Type.[isbuiltin, is_builtin, is_built_in, builtin] →    bool 
     102        Returns whether the type is built-in. 
     103Type.[refs, ref, references] →  Type 
     104        The references of the type. 
     105Type.[paramsref, paramsrefs] →  Type 
     106        The references of the types arguments. 
     107}}} 
     108 
     109=== Other selectors: === 
     110{{{ 
     111FunParam.type →         Type 
     112        Possible types of the argument according to the spec. 
     113 
     114RecordField.[type, types] → Type 
     115        The type or type expression of the given record field. 
     116}}} 
     117 
     118== Returned values for new internal types in sq: == 
     119As it can be seen in the above description, for the practical use of analyzed type informations, new internal types(spec, specparam, type) have been introduced for semantic queries. When a query isn't continued with other selectors to reach conventional types, it might not be obvious what the results would be. In case of our text-based and graphical interfaces, the format of the shown result for each of these types is as follows: 
     120 
     121{{{ 
     122Spec -> string (textual representation of the -spec attribute = Spec.text) 
     123 
     124SpecParam -> string (textual representation for the type expression of the spec-parameter = SpecParam.text) 
     125 
     126Type -> string (Module:Name/Arity | Text) 
     127Module -> atom (defining module of the type) 
     128Name -> atom (name of the type = Type.name) 
     129Arity -> int (number of parameters of the type = Type.arity) 
     130Text -> string (textual representation of the type expression) 
     131}}} 
     132 
     133Examples: 
     134{{{ 
     135ri:q("mods.specs"). 
     136a.erl 
     137    -spec g(prp_user(X,Y))->'ok'. 
     138... 
     139 
     140ri:q("mods.specs.params"). 
     141-spec g(prp_user(X,Y))->'ok'. 
     142    prp_user(X,Y) 
     143... 
     144 
     145ri:q("mods.types"). 
     146a.erl 
     147    a:prp_user/2 
     148    a:prp_user3/2 
     149... 
     150}}} 
     151 
     152Our scriptable interface, along with the textual representations, also provides the unique entities of the stored type informations, which might be used to differentiate between results even when their formatted values are the same. 
     153 
     154{{{ 
     155ris:q("mods.specs.params.type.params"). 
     156{rich,"a:prp_user/2\n    Key\n     Val\nb:prp_user/2\n    Key\n     Val\nc:prp_user/2\n    Key\n     Val\n", 
     157      [{entity,{'$gn',typexp,151}}, 
     158       {entity,{'$gn',typexp,152}}, 
     159       {entity,{'$gn',typexp,296}}, 
     160       {entity,{'$gn',typexp,297}}, 
     161       {entity,{'$gn',typexp,439}}, 
     162       {entity,{'$gn',typexp,440}}]} 
     163}}} 
     164 
     165== Detailed review of non-trivial selectors: == 
     166 
     167During the specification we have tried our best to keep things simple. This meant we would not differentiate between namedtypes(user-defined, built-in) and type expressions. In some cases this have resulted in rather complex specifications which can be seen below. Under normal circumstances, the different behaviors should be intuitively evident and would not cause any surprise. 
     168 
     169=== File.typerefs: === 
     170This selector returns all types referenced in the file. It does not return type expressions, but it returns all other types(even built-in types or ones which have been defined in an other file) that have been referenced in "-spec", "-type", "-opaque" attributes or record definitions. 
     171{{{ 
     172ri:q("mods.typerefs"). 
     173a.erl 
     174    erlang:atom/0 
     175    a:prp_user/2 
     176}}} 
     177 
     178=== Function.returntype(, Spec.returntype): === 
     179Returns types or type expressions present as the return-type of the function's specs. It does not split union types, but please note that specs can be overloaded so even functions with only one clause can have multiple values. (Single-atom type expressions are filtered.) 
     180{{{ 
     181ri:q("mods.funs.returntypes"). 
     182a:g/2 
     183    erlang:atom/0 
     184}}} 
     185 
     186=== Spec.refmod: === 
     187Specs can have module-identifiers which determine the module whose function is being specified. This module is returned by the selector unless the module-identifier was omitted, in which case the defining module is returned. 
     188{{{ 
     189ri:q("mods.funs.spec.refmod"). 
     190-spec io:format(string(), list())->string(). 
     191    io 
     192}}} 
     193 
     194=== Spec.arguments: === 
     195Returns the type expression for the arguments of the specification. Argument-names are included. 
     196{{{ 
     197ri:q("mods.funs.spec.params"). 
     198-spec io:format(Format :: string(), list())->string(). 
     199    Format :: string() 
     200     list() 
     201}}} 
     202 
     203=== Spec.guardtypes: === 
     204Returns the type expressions of spec-guards. 
     205{{{ 
     206ri:q("mods.funs.spec.guardtypes"). 
     207-spec io:format(Format :: string(), D)->string() when D :: list(string()). 
     208     D :: list(string()) 
     209}}} 
     210 
     211=== Specparam.type: === 
     212Returns the type or type expression of the given specparam. (Single-atom type expressions are filtered.) 
     213{{{ 
     214ri:q("mods.funs.spec").            
     215io:format/2 
     216    -spec io:format(string(), list)->string() when D :: list(string()). 
     217 
     218ri:q("mods.funs.spec.param.type"). 
     219string() 
     220    erlang:string/0 
     221}}} 
     222 
     223=== Specparam.name: === 
     224Returns the name of the specparam as a string. If the name was omitted, returns "undefined". 
     225{{{ 
     226ri:q("mods.funs.spec").            
     227io:format/2 
     228    -spec io:format(Format :: string(), list()) ->string(). 
     229ok 
     230ri:q("mods.funs.spec.param.name"). 
     231Format :: string() 
     232    name = "Format" 
     233 list() 
     234    name = "undefined" 
     235}}} 
     236 
     237=== Type.params: === 
     238Returns the type or type expression of the given type's parameters. (Single-atom type expressions are filtered.) 
     239{{{ 
     240%%-type myt(Key, Val) :: myt(Key, ok). 
     241ri:q("mods.types.params").            
     242a:myt/2 
     243    Key 
     244     Val 
     245ok 
     246ri:q("mods.types.subtypes.params"). 
     247 myt(Key, ok) 
     248    Key 
     249}}} 
     250 
     251=== Type.subtypes: === 
     252If the given type is a user-defined type it returns the type's body as a type expression. When the given type is a type expression it returns the expression's subexpressions(1-level deep). (Single-atom type expressions are filtered.) 
     253{{{ 
     254ri:q("mods.types.subtypes"). 
     255a:prp_user/2 
     256     [{atom(), Key, Val}] 
     257a:myt/2 
     258     myt(Key, ok) 
     259ok 
     260ri:q("mods.types.subtypes.subtypes"). 
     261 [{atom(), Key, Val}] 
     262    {atom(), Key, Val} 
     263 myt(Key, ok) 
     264    Key 
     265}}} 
     266 
     267=== Type.specrefs: === 
     268For user-defined and built-in types, returns the specs referencing this type. 
     269{{{ 
     270ri:q("mods.types.specrefs"). 
     271b:prp_user/2 
     272    -spec g(prp_user(X,Y))->'ok'. 
     273}}} 
     274 
     275=== Type.references: === 
     276Returns the type expressions using this user-defined or built-in type. If the given type is a type expression it tries to find the user-defined or built-in type referenced in the type expression and continues from there. Otherwise the result is empty. 
     277{{{ 
     278ri:q("mods.types.refs").                   
     279b:prp_user/2 
     280    prp_user(X,Y) 
     281a:myt/2 
     282     myt(list(), ok) 
     283     myt(Key, ok) 
     284}}} 
     285 
     286== Practical example: == 
     287Let's say we are new to a project with a large code-base and our task is to implement a new interface in which we have to do some operations on a very complex data structure. The source of this complex structure is remote, but we know there are multiple compatible data structures already implemented, although not sure about the specifics, so we would like to investigate and do this without manually going through several thousand lines of code. 
     288 
     289We might start by running the following query: 
     290{{{ 
     291"files.typerefs" 
     292}}} 
     293This gives us all used types in every file. After scrolling to the end of the hundred and something pages of results, we at least know types properly specified, but not much else. 
     294Maybe we should only look at types defined in "general" modules: 
     295{{{ 
     296"mods[name ~ \"gen\"].types" 
     297}}} 
     298Unfortunately these seem to be either irrelevant or too general for us to use. We decide that we should look at types from other modules by filtering results using the "-spec" attributes (and let's assume, we are looking for a structure that has an "append" function implemented). 
     299But first we would like to know if "-spec" is used in our modules. 
     300{{{ 
     301"mods.funs[not (.name in files.specs.name)][loc > 10]" 
     302}}} 
     303The query results are promising, it looks like most of the non-trivial functions have a specification. 
     304We can take a look at returned types of functions named like "append": 
     305{{{ 
     306mods.funs[name ~ \"append\"].returntype 
     307}}} 
     308This query also gives us too many possible results, we are only interested in exported ones: 
     309{{{ 
     310mods.funs[name ~ \"append\"].returntype[exported] 
     311}}} 
     312Turns out most of them were exported. However when we have looked at types from "gen" modules, we may have noticed a type("person") for a structure which is a part of what we have to append to the large structure. 
     313{{{ 
     314mods.funs[name ~ \"append\"][.params.type.(subtype.(params)+)+[name = 'person']].returntype[exported] 
     315}}} 
     316This query only yields return-types of ~"append" functions that have a parameter whose type uses the "person" type. 
     317If we would like to see in which files these types have been used we could run the following: 
     318{{{ 
     319mods.funs[name ~ \"append\"][.params.type.(subtype.(params)+)+[name = 'person']].returntype[exported].references.file 
     320}}} 
     321 
     322== Limitations: == 
     323 - For performance and readability reasons, type-expressions consisting of a single atom(e.g., bool, int, 'error') are filtered from results. This means, without re-parsing the textual representations, the results can not be used perform reliable type checking or comparison for functions. (We believe, including such simple expressions in results for an ordinarily large code-base would undermine the main goal of these features, which is providing an easy way for understanding type informations and their relation.) 
     324 - As of now, "@type" and "@spec" declarations are not analyzed.