Identifying code regions in shifting tides.
This is a library that interprets external annotations (in spec files) which
identify source location ranges through descriptors (called entries) leveraging
various backends to find the actual line:column absolute source locations.
Some backends are more expressive and can identify all regions corresponding to a specific pattern, others are designed to only match a specific region relative to an Ada declaration, or maybe the need is only to be able to add external annotations to a frozen code base, in which case an absolute source location range may work well.
The library allows attaching some form of information to the identified regions, the meaning of which is entirely up to the tool using the library.
Stable_Sloc currently depends on:
- Ada-toml (master)
- GNATCOLL-core
- Libadalang (and langkit_support)
Build using either gprbuild, or the Makefile (a convenience wrapper around
gprbuild and gprinstall), Stable_Sloc is written in Ada 2022.
Run make build to build the library itself and the CLI tool
Annotations are described in TOML files. Each element of the root table is considered to be an entry, the key being its unique identifier. Each entry consist of the following fields
-
annotations, an array of tables. Used to provide context for the entry. Interpretation is left to the tool.Each table in the array may contain a
purposefield, which can be used to filter which entry should be matched or not using thepurpose_prefixparameter in theMatch_Entriesfunction, or the--filterswitch on the CLI. -
file, a string containing a globing pattern of files on which this entry should be searched for.This field is optional, if not specified or if it is equal to the empty string, the entry will be active on all files.
-
at_most_once, a boolean specifying wether the entry is expected to match more than once. IfTrue, once the entry is loaded, it will return a failed Match_Result upon every match once a successful match has been found.This is optional and defaults to
False -
kind, a string, defining which source location matcher will be used to interpret this entry. -
matcher, a table containing the required fields by the specific source location matcher.
Other fields will be ignored.
There are currently three matchers built into the library, with more to come.
It represents an absolute source location and will not try to compensate source changes. It is provided as a simple last resort matcher, only suitable for frozen code bases.
Entry fields:
-
kind="absolute" -
start_line, required, integer.Line of the beginning of the location range.
-
start_col, required, integer.Column of the beginning of the location range.
-
end_line, required, integer.Line of the end of the location range.
-
end_col, required, integer.Column of the end of the location range.
-
sha256, optional, string. Hex representation of the SHA256 digest of the file in which the designated location lies. Useful to ensure an absolute entry no longer matches if a file is modified.
When matching an absolute entry, a check is made on each file to which the matcher applies to ensure the lines/columns described in the entry fit in the current content of the file. If the sha256 field is set, the matcher will not produce a successful match if the candidate's file SHA256 digest does not match the one stored in the entry.
This matcher supports updates (-u switch on the command line)
It matches a location range through a regular expression. The regular expression is used to search through the whole content of a file, including the newline characters, and not line by line.
Entry fields:
-
kind="regexp" -
regexp, required, string.Regular expression to be matched. Uses the
GNAT.Regpatpackage as a regular expression matching backend, see the specification of that package for the recognized grammar. -
case_insensitive, optional, boolean.If
true, The automaton is optimized so that the matching is done in a case insensitive manner (upper case characters and lower case characters are all treated the same way). Default tofalseif not present. -
single_line, optional, boolean.If
true, treat the file content as a single line. This means that^and$will ignore\n(unlessmulti_lineis alsotrue), and that'.'will match\n. Defaults tofalseif not present. -
multi_line, optional, boolean.If
true, treat the file content as multiple lines. This means that^and$will also match on internal newlines (ASCII.LF), in addition to the beginning and end of the file. Defaults tofalseif not present.
This matcher recognizes the code region from a Libadalang node designating the inner-most named declaration fully containing the designated location range, a context hash ensuring the contents of the declaration has not changed, and a relative source location to that declaration. It is thus stable to any modification of the sources, provided the identified declaration does not change package or nesting level, and that the local contents of the declaration do not change (trivia included).
This matcher is not indented to be written by humans, but instead to be generated using the library or cli.
Entry Fields:
-
kind=lal_context -
sem_parents, required, array of strings.Contains the list of lower-cased named entities containing the declaration used as context, in increasing depth order. The last element is the defining name of the context declaration. Concatenated with '.' it would spell the context declaration's fully qualified name.
-
content_hash, required, string.Hash of the text content of the context declaration node. It must be formatted It must be formatted as a 32 bit hexadecimal number, in Ada numerical syntax:
16#ABCDEF12# -
The same fields as in an absolute matcher, which represent the relative location range to the beginning of the context declaration.
The library is extensible, you can write your own matcher and register it using
the API in Stable_Sloc.Matchers.
A Matcher must simply implement the Stable_Sloc.Matchers.Sloc_Matcher
interface, and you must provide a callback to parse a TOML entry to produce a
matcher object for that specific entry. The callback can be registered through
the Stable_Sloc.Matchers.Register_Matcher procedure, and proceed to use the
library as if your matcher was a builtin one.