Some functions cannot be fully type checked, because our API allows
fully arbitrary kwargs and treats them as data to pass through to the
underlying feature. For example, hotdoc command line arguments.
This change allows us to type check some kwargs with known types and
possibly required status, and make their values consistent(ly defaultable),
while preserving the optional nature of the additional kwargs.
This can be triggered if someone tries to call a non-ID. The example
reproducer was:
```
if (var = dependency(...)).found()
```
This produced a traceback ending in
```
raise InvalidArguments(f'Variable "{object_name}" is not callable.')
UnboundLocalError: local variable 'object_name' referenced before assignment
```
After this commit, the error is reported as:
```
ERROR: AssignmentNode is not callable.
```
Given a kwarg value that is itself a dict/list, we would only check if
the since_values was present within that container. If the since_values
is itself the dict or list type, then we aren't looking for recursive
structures, we just want to know when the function began to accept that
type.
This is relevant in cases where a function accepted a dict, and at one
point began accepting a list (of strings in the form 'key=value'), or
vice versa.
"targetting" is verb-derived adjective, which sort-of-works here, but
makes the whole sentence awkward, because there's no verb. Let's just
use present simple.
"tried to use" implies that the attempt was not successful, i.e. that meson
ignored the feature. But that is not what happens, apart from the warning the
feature works just fine. The new message is also shorter ;)
+ Extend the parser to recognize the multiline f-strings, which the
documentation already implies will work.
The syntax is like:
```
x = 'hello'
y = 'world'
msg = f'''This is a multiline string.
Sending a message: '@x@ @y@'
'''
```
which produces:
```
This is a multiline string.
Sending a message: 'hello world'
```
+ Added some f-string tests cases to "62 string arithmetic" to exercise
the new behavior.
These are only used for type checking, so don't bother importing them at
runtime.
Generally add future annotations at the same time, to make sure that
existing uses of these imports don't need to be quoted.
Using future annotations, type annotations become strings at runtime and
don't impact performance. This is not possible to do with T.cast though,
because it is a function argument instead of an annotation.
Quote the type argument everywhere in order to have the same effect as
future annotations. This also allows linters to better detect in some
cases that a given import is typing-only.
A bunch of files have several T.TYPE_CHECKING blocks that each do some
things which could just as well be done once, with a single `if`
statement. Make them do so.
The point of a .use() function is because we don't always have the
information we need to use a feature check, so we allow creating the
feature and then storing it for later use. When implementing location
checks, although it is optional, actually using it violated that design.
Move the location out of the init method for FeatureCheck itself. It
remains compatible with all cases of .single_use(), but fix the rest up.
Use a derived type when passing `subproject` around, so that mypy knows
it's actually a SubProject, not a str. This means that passing anything
other than a handle to the interpreter state's subproject attribute
becomes a type violation, specifically when the order of the *four*
different str arguments is typoed.
In some cases, init variables that accept None as a sentinel and
immediately overwrite with [], are migrated to dataclass field
factories. \o/
Note: dataclasses by default cannot provide eq methods, as they then
become unhashable. In the future we may wish to opt into declaring them
frozen, instead/additionally.
FeatureCheck always immediately sets extra_message to '' if it isn't
explicitly passed, so there is really no point in using None as a
sentinel that is never used.
Names used in init functions are sometimes pointlessly different from
the class instance attributes they are immediately assigned to. They
would make more sense if defined properly.
Currently, if you pass a `[]string`, but the argument expects
`[]number`, then you get a message like `expected list[str] but got
list`. That isn't helpful. With this patch arrays and dictionaries will
both print messages with the types provided.
The inner closure of the typed_kwargs function is already complicated
enough without defining closures in the middle of a loop. Let's just
pass the types_tuple as an argument to both avoid redefining the
function over and over, and also make the whole thing easier to read.
Since these aren't warnings, per se, we don't note every single call
site that has one. And we raise mlog.notice in non-fatal mode to avoid
either:
- being too threatening
- making builds fail with --fatal-meson-warnings
Nevertheless, it is useful to give people a heads-up that there is an
upgrade opportunity, rather than waiting until they upgrade and then
causing projects to begin printing fatal warnings.
mlog can already print location info, and we use this often -- including
for custom feature warnings already. Make this work everywhere, so that
it is feasible to move such custom warnings to globally tracked
Features.
info.types could be a tuple like (str, ContainerTypeInfo()). That means
we have to check types one by one and only print error if none of them
matched.
Also fix the case when default value is None for a container type, it
should leave the value to None to be able to distinguish between unset
and empty list.
This is the final refactoring for extracting the bultin object
logic out of Interpreterbase. I decided to do both arrays and
dicts in one go since splitting it would have been a lot more
confusing.
I've used any because it needs to be infinitely recursive, something
that we simply can't model. But basically until it goes into validator
we have no way of knowing what's going on, since one can write code
like:
```python
KwargInfo[str]('arg', object, validator=_some_very_complex_logic_to_get_specific_string)
```
As such, we can't assume that validator is receiving a type _T, it could
be anything.
Another commit in my quest to rid InterpreterBase from all higher
level object processing logic.
Additionally, there is a a logic change here, since `str.join` now
uses varargs and can now accept more than one argument (and supports
list flattening).
Since it cannot resolve `import typing as T` in order to figure out that
T.* is doing annotation-worthy stuff.
Since T.cast('Foo') is not actually using Foo except in an annotation
context (due to being a string) it requires extra work to resolve, and
the only thing that would currently work is actually using
'typing.cast'. However, we have decided to not use it except as T...
Since this import is only imported during mypy it's not so bad to noqa
it.