5. Advice & gotchas
This section of the manual regroups together guidance that we consider vital to develop with Libadalang, but unlike the tutorial, this is meant as a grab bag that will be useful to new developers as well as experienced Libadalang developers. We advise people using Libadalang to come back to this section from time to time to see if there are new advices & gotchas.
5.1. Ada parsing: Identifiers starting with a $
Historically, Libadalang has parsed identifiers starting with a dollar ($
)
as regular identifiers, even though it is not valid Ada. The aim for that was
to provide minimal support for GNATprep style preprocessing.
However, nowadays, full support for preprocessing has been implemented in
Libadalang, via the File_Readers
and
Preprocessing
APIs. If you use those,
identifiers starting with $
will be interpreted as preprocessor
identifiers. Else, they will just be parsed normally.
Despite Libadalang having a real preprocessor, this behavior has been kept for backward compatibility with some LAL projects, notably Ada-Renaissance, and will be preserved onwards.
5.2. Ada API: Up & down casting Ada_Node
instances
In Analysis
, a hierarchy of tagged types is declared,
rooting in the Ada_Node
type. It is important
to understand that those node types are equivalent to a class wide access to a
node.
They’re in reality kind of a fat pointer, containing a pointer to the syntactic node, and some more context, like generic instantiation information.
A consequence of this design choice is that you cannot use the regular Ada conversion functions to up/down cast node types:
1 A : Ada_Node := Unit.Root;
2
3 -- This code is invalid and will raise an error
4 B : Compilation_Unit := Compilation_Unit (A);
5
6 -- This code is valid and will work as you expect
7 C : Compilation_Unit := A.As_Compilation_Unit;
5.3. Ada API: Equality between Ada_Node
and derived types
5.3.1. Summary
As said previously, nodes types are like fat pointers to a node, something like
(pointer_to_node, more_entity_info)
.
For complex reasons we’re exposing an "="
operator on classwide nodes, and
that’s the one you’ll use by default, but when instantiating generic containers
with nodes, if you need an equivalent function, you cannot use "="
, because
that will use the default equality operator. Instead, you need to use the
Ada_Node.Equals
function:
with Libadalang.Analysis; use Libadalang.Analysis;
...
package Node_Sets is new Ada.Containers.Hashed_Sets
(Element_Type => Ada_Node,
Hash => Hash,
Equivalent_Elements => Equals,
"=" => Equals);
5.4. Standard unit peculiarities
In Ada, the Standard
package is special: is acting such as a built-in
compilation unit and is the root of all other units: these are technically
child units of Standard
. It is the package that defines “native”
declarations such as Boolean
, Integer
but also Character
,
Wide_Wide_String
and the Constraint_Error
exception.
Libadalang implements this package as a special analysis unit, heavily inspired
from GNAT’s own implementation of this package (see GNAT’s -gnatS
flag):
package Standard is
pragma Pure (Standard);
type Boolean is (False, True);
type Integer is range L .. H;
subtype Natural is Integer range L .. H;
subtype Positive is Integer range L .. H;
type Short_Short_Integer is range L .. H;
type Short_Integer is range L .. H;
type Long_Integer is range L .. H;
type Long_Long_Integer is range L .. H;
type Short_Float is digits D range L .. H;
type Float is digits D range L .. H;
type Long_Float is digits D range L .. H;
type Long_Long_Float is digits D range L .. H;
type Character is ('A');
type Wide_Character is ('A');
type Wide_Wide_Character is ('A');
package ASCII is
NUL : constant Character := Character'Val (16#00#);
SOH : constant Character := Character'Val (16#01#);
STX : constant Character := Character'Val (16#02#);
ETX : constant Character := Character'Val (16#03#);
EOT : constant Character := Character'Val (16#04#);
ENQ : constant Character := Character'Val (16#05#);
ACK : constant Character := Character'Val (16#06#);
BEL : constant Character := Character'Val (16#07#);
BS : constant Character := Character'Val (16#08#);
HT : constant Character := Character'Val (16#09#);
LF : constant Character := Character'Val (16#0A#);
VT : constant Character := Character'Val (16#0B#);
FF : constant Character := Character'Val (16#0C#);
CR : constant Character := Character'Val (16#0D#);
SO : constant Character := Character'Val (16#0E#);
SI : constant Character := Character'Val (16#0F#);
DLE : constant Character := Character'Val (16#10#);
DC1 : constant Character := Character'Val (16#11#);
DC2 : constant Character := Character'Val (16#12#);
DC3 : constant Character := Character'Val (16#13#);
DC4 : constant Character := Character'Val (16#14#);
NAK : constant Character := Character'Val (16#15#);
SYN : constant Character := Character'Val (16#16#);
ETB : constant Character := Character'Val (16#17#);
CAN : constant Character := Character'Val (16#18#);
EM : constant Character := Character'Val (16#19#);
SUB : constant Character := Character'Val (16#1A#);
ESC : constant Character := Character'Val (16#1B#);
FS : constant Character := Character'Val (16#1C#);
GS : constant Character := Character'Val (16#1D#);
RS : constant Character := Character'Val (16#1E#);
US : constant Character := Character'Val (16#1F#);
DEL : constant Character := Character'Val (16#7F#);
Exclam : constant Character := '!';
Quotation : constant Character := '""';
Sharp : constant Character := '#';
Dollar : constant Character := '$';
Percent : constant Character := '%';
Ampersand : constant Character := '&';
Colon : constant Character := ':';
Semicolon : constant Character := ';';
Query : constant Character := '?';
At_Sign : constant Character := '@';
L_Bracket : constant Character := '[';
Back_Slash : constant Character := '\';
R_Bracket : constant Character := ']';
Circumflex : constant Character := '^';
Underline : constant Character := '_';
Grave : constant Character := '`';
L_Brace : constant Character := '{';
Bar : constant Character := '|';
R_Brace : constant Character := '}';
Tilde : constant Character := '~';
LC_A : constant Character := 'a';
LC_B : constant Character := 'b';
LC_C : constant Character := 'c';
LC_D : constant Character := 'd';
LC_E : constant Character := 'e';
LC_F : constant Character := 'f';
LC_G : constant Character := 'g';
LC_H : constant Character := 'h';
LC_I : constant Character := 'i';
LC_J : constant Character := 'j';
LC_K : constant Character := 'k';
LC_L : constant Character := 'l';
LC_M : constant Character := 'm';
LC_N : constant Character := 'n';
LC_O : constant Character := 'o';
LC_P : constant Character := 'p';
LC_Q : constant Character := 'q';
LC_R : constant Character := 'r';
LC_S : constant Character := 's';
LC_T : constant Character := 't';
LC_U : constant Character := 'u';
LC_V : constant Character := 'v';
LC_W : constant Character := 'w';
LC_X : constant Character := 'x';
LC_Y : constant Character := 'y';
LC_Z : constant Character := 'z';
end ASCII;
type String is array (Positive range <>) of Character;
pragma Pack (String);
type Wide_String is array (Positive range <>) of Wide_Character;
pragma Pack (Wide_String);
type Wide_Wide_String is array (Positive range <>) of Wide_Wide_Character;
type Duration is delta D range L .. H;
for Duration'Small use S;
type Universal_Int_Type_ is range -1 .. 1;
type Universal_Real_Type_ is digits 16;
type Universal_Fixed_Type_ is digits 16;
package root_types_ is
type root_integer is range -1 .. 1;
type root_real digits 16;
end root_types_;
Constraint_Error : exception;
Program_Error : exception;
Storage_Error : exception;
Tasking_Error : exception;
Abort_Signal : exception;
end Standard;
Warning: this is a stub, so don’t rely on the implementation using this specific source as it could change in future versions of Libadalang.
Also be aware that, in order for Libadalang to use the right values for range
bounds, digits and 'Small
, it is necessary to communicate the relevant
target information (Libadalang.Target_Info
) to Libadalang’s analysis
context, either calling Libadalang.Analysis.Set_Target_Information
, or
letting Libadalang.Analysis.Create_Context_From_Project
fetch this
information from the project file.
Note that the Character
, Wide_Character
and Wide_Wide_Character
types are represented with partial definitions:
type Character is ('A');
type Wide_Character is ('A');
type Wide_Wide_Character is ('A');
Defining all values for each is not realistic, as for instance
Wide_Wide_Character
has 4 billion values: it is not reasonable to allocate
memory for all of them. Nevertheless, undefined characters are properly
supported and synthesized on-demand, which means that any
characters from any sets can be properly handled by Libadalang.
5.5. The origin
parameter
In Libadalang many semantic properties, like
P_Referenced_Decl
, have an origin
parameter.
That’s because for many semantic queries, the answer to your query might differ
depending on where you come from.
For example in the following code:
package P is
type T is private;
private
type T is record
A, B: Integer;
end record;
end P;