How to find out what type a rustc::middle::ty::Ty represents? - plugins

For writing yet another lint in Rust, I need to make sure that the type of an Expr is actually an Option<_> (or any pointer to one). I have already walked any ptrs and rptrs to their conclusion and am left with a rustc::middle::ty that in my test case debugs to (manually formatted for better readability):
TyS {
sty: ty_enum(
DefId {
krate: 2,
node: 117199
},
Substs {
types: VecPerParamSpace {
TypeSpace: [
TyS {
sty: ty_int(i32),
flags: 0,
region_depth: 0
}
],
SelfSpace: [],
FnSpace: [],
},
regions: NonerasedRegions(
VecPerParamSpace {
TypeSpace: [],
SelfSpace: [],
FnSpace: [],
}
)
}
),
flags: 0,
region_depth: 0
}
However, now I'm a bit lost – how do I find out if the TyS is actually an Option<_> type?

You need use with_path on the DefId. You will be provided an iterator over PathElems which you must consume.
The following is a rough sketch, but should give you an array of Names if you tweak it a bit.
if let ty_enum(did, ..) = ty.sty {
tcx.with_path(did, |iter| iter.map(|elem| elem.name())).collect::<Vec<Name>>;
}

Related

I am trying to use a List within dart to Store information

How can I retrieve this information as when I am testing it using print it is returning the following error?
I have searched online to try and find a solution but alas for the past 30 minutes i have not been able to find one. If there is an easier and more efficient way of storing information then please let me know.
Apologies as I am still learning dart, moving from JS.
List<Object> chosenSelection = [
[
'Advanced Higher', //[0][0]
false /**Enabled? */, //[0][1]
[
'Style',
'MelodyHarmony',
'RhythmTempo',
'TextureStructureForm',
'Timbre',
] /**Categories */, //[0][2][0-4]
],//[0]
[
'Higher', //[0][0]
false /**Enabled? */, //[0][1]
[
'Style',
'MelodyHarmony',
'RhythmTempo',
'TextureStructureForm',
'Timbre',
] /**Categories */, //[0][2][0-4]
]
];
print(chosenSelection[0][0]); //Output: 'Advanced Higher'
print (chosenSelection[0][2][4]); // Output 'Timbre'
Resulting error:
type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'List' of 'function result'
I am trying to access the data 'Advanced Higher' and then 'Timbre' within the variable.
I'm not sure what's the question here, but this code doesn't compile for 2 reasons:
You're missing a closed bracket in the second print;
You're trying to access the elements of an Object as if it is a nested List.
You are trying to implement a 2d and 3d list. But you have declared a 1d list.
In order to declare a 2d list you need to declare it as List<List<Object>> and for a 3d list: List<List<List<Object>>>
So you need to declare List<List<List<Object>>> chosenSelection, and then
print(chosenSelection[0][0]); //Output: 'Advanced Higher'
print (chosenSelection[0][2][4]); // Output 'Timbre'
I ended up settling for making the information part of a class.
class ChosenLevel {
String level;
bool overallToggled = false;
Map categories = {
'Style': true,
'MelodyHarmony': false,
'RhythmTempo': false,
'TextureStructureForm': false,
'Timbre': false,
};
Color _color;
ChosenLevel(this.level, this._color);
get categoriesNumber {
return categories.length;
}
specificCategory(index) {
switch (index) {
case 0:
return 'Style';
case 1:
return 'MelodyHarmony';
case 2:
return 'RhythmTempo';
case 3:
return 'TextureStructureForm';
case 4:
return 'Timbre';
/* default:
throw ("Something strange happend"); */
}
}
toggleCat(c) {
this.categories['${c}'] = !this.categories['${c}'];
}
toggleLevel() {
this.overallToggled = !this.overallToggled;
}
#override
String toString() {
return '{${level}, ${categories}}';
}
}

Babel traverse to the first JSXElement

I need to add a custom attribute to components of a ReactJS app, for testing purposes. I have tried to use 2 different plugins, however, they apply the attribute to all JSXElements within that component and I want to apply it to just the first, otherwise, I end up with unnecessary duplication of the attribute which makes writing the tests even harder and I'm trying to save fragility.
I have gone through the docs and cannot find how to traverse to the first JSXElement, so have tried a couple of things myself and failed, these are
The below never adds the attribute
visitor: {
JSXElement( path, state ) {
if ( path.key === 0 )
{
path.node.openingElement.attributes.push(
t.jSXAttribute(
t.jSXIdentifier( 'data-e2e' ),
t.stringLiteral( `${someValue}` )
)
);
}
}
}
The below throughs error that firstElem in undefined
visitor: {
Program( path, state ) {
let arr = [];
path.traverse( {
enter( jsxPath )
{
if ( jsxPath.node.type === 'JSXElement' )
{
arr.push( jsxPath );
}
}
} );
let firstElem = arr[ 0 ]; <<< undefined
firstElem.node.openingElement.attributes.push(
t.jSXAttribute(
t.jSXIdentifier( 'data-e2e' ),
t.stringLiteral( `${someValue}` )
)
);
}
}
}
Can anyone suggest how to get just the first JSXElement.
So I found the answer by pure luck. I should have added in a stop() immediately after traversing the JSXElement
JSXElement( jsxPath )
{
jsxPath.stop();
...
The stop() function is not that will documented, in fact, none of BabelJS is that will documented for a beginner. Found this by looking at a someone's plugin and wondering what it was doing there and by trial and error, found it resolved the above propblem.

How to replace function name with a babel plugin

Is it possible to create a babel plugin that will change some a functions name ?
I can't seems to find this in the documentation.
Example:
myObject.doSomething() ==> babel ==> myObject.___doSomething()
Thanks
You can get the AST of your code in astexplorer. And you can see it's about a CallExpression and MemberExpression. So search babel-types API in babel-types source code, it's very clear of how to create a babel type or judge a babel-type like this:
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
Following are two different ways to do it (either with the Program visitor or with the FunctionDeclaration visitor):
export default function ({types: t}) {
return {
visitor: {
Program(path) {
path.scope.rename('doSomething', '___doSomething');
},
FunctionDeclaration(path) {
if (path.node.id.name === 'doSomething') {
path.node.id.name = '___doSomething'
}
}
}
};
}
Note that these are not safe since they can override an existing name. You can use the path.scope.generateUidIdentifier("uid"); command to generate a unique identifier and use that but you wont be able to define the generated name.
Example - http://astexplorer.net/#/o5NsNwV46z/1

Counting length of repetition in macro

I'm trying to implement a macro to allow MATLAB-esque matrix creation. I've got a basic working macro but I still have a long way to go.
I want to be able to enforce the right structure (same number of elements in each row) but I'm not sure how to do this within the macro. I think I want to enforce that each internal repetition has the same length - is this something I can do?
Here is my code so far:
pub struct Matrix<T> {
pub cols: usize,
pub rows: usize,
pub data: Vec<T>
}
macro_rules! mat {
( $($( $x:expr ),*);* ) => {
{
let mut vec = Vec::new();
let mut rows = 0;
$(
$(
vec.push($x);
)*
rows += 1;
)*
Matrix { cols : vec.len()/rows, rows: rows, data: vec}
}
};
}
It works but as you can see isn't very safe. It has no restrictions on the structure.
I want to do a lot more with this macro but I think this is a good start!
Update:
Here is some playground code for a crappy implementation I worked out. If anyone has any better suggestions please let me know! Otherwise I'll close this myself.
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
}
macro_rules! mat {
( $( $x:expr ),* ) => { {
let vec = vec![$($x),*];
Matrix { cols : vec.len(), rows: 1, data: vec }
} };
( $( $x0:expr ),* ; $($( $x:expr ),*);* ) => { {
let mut _assert_width0 = [(); count!($($x0)*)];
let mut vec = Vec::new();
let rows = 1usize;
let cols = count!($($x0)*);
$( vec.push($x0); )*
$(
let rows = rows + 1usize;
let _assert_width = [(); count!($($x)*)];
_assert_width0 = _assert_width;
$( vec.push($x); )*
)*
Matrix { cols : cols, rows: rows, data: vec }
} }
}
playground
The count! macro expands to a constant expression that represents the number of arguments it got as input. It's just a helper for the mat! macro. If you need to count a lot of items and the compiler can't cope with it, see the Counting chapter in The Little Book of Rust Macros, which has more complex macros for counting.
My version of the macro uses dummy variables and assignments to verify that the width of all rows are the same. First off, I changed the macro's pattern to handle the first row separately from the subsequent rows. The first variable, _assert_width0, is initialized with an array of units ((), which makes the array take no memory), with the size of the array being the number of items in the first row. Then, _assert_width is also initialized with an array of units, with the size of the array being the number of items in each subsequent row. Then, _assert_width is assigned to _assert_width0. The magic here is that this line will raise a compiler error if the width of a row doesn't match the width of the first row, since the types of the array won't match (you might have e.g. [(); 3] and [(); 4]). The error isn't super clear if you don't know what's going on in the macro, though:
<anon>:38:24: 38:37 error: mismatched types:
expected `[(); 3]`,
found `[(); 4]`
(expected an array with a fixed size of 3 elements,
found one with 4 elements) [E0308]
<anon>:38 _assert_width0 = _assert_width;
^~~~~~~~~~~~~
<anon>:47:13: 47:44 note: in this expansion of mat! (defined in <anon>)
<anon>:38:24: 38:37 help: see the detailed explanation for E0308
First, to quickly address the title of your question: see the Counting chapter in The Little Book of Rust Macros. To summarise: there is no direct way, you need to write a macro that expands to something you can count in regular code.
Now, to address your actual question: hoo boy.
It's not so much counting that you want, it's to fail at compile time if the sub-sequences have different lengths.
First of all, there's no clean way to trigger a compilation failure from a macro. You can trigger some other pre-existing error, but you can't control the actual error message.
Secondly, there's no easy way to do "variable" comparisons in macros at all. You can sometimes compare against a fixed token sequence, but you're not doing that here.
So it's doubly not-really-doable.
The simplest thing to do is check the lengths during construction at runtime, and return an error or panic if they don't match.
Is it actually impossible? I don't believe so. If you're willing to accept inscrutable error messages and a massive jump in complexity, you can check for length equality between two token sequences like so:
macro_rules! tts_equal_len {
(($_lhs:tt $($lhs_tail:tt)*), ($_rhs:tt $($rhs_tail:tt)*)) => {
tts_equal_len!(($($lhs_tail)*), ($($rhs_tail)*))
};
(($($_lhs_tail:tt)+), ()) => { do_something_bad!() };
((), ($($_rhs_tail:tt)+)) => { do_something_bad!() };
((), ()) => { do_something_good!() };
}
macro_rules! do_something_bad { () => { { println!("kaboom!") } } }
macro_rules! do_something_good { () => { { println!("no kaboom!") } } }
fn main() {
tts_equal_len!((,,,), (,,,));
tts_equal_len!((,,,), (,,));
tts_equal_len!((,), (,,));
}
Again, the real problem is finding some way to fail at compile time such that the user will understand why compilation failed.
Update: there's a new way of doing things
As of the day on which this was written, the feature of rust which enables the following (count) to be done, in still unstable and is available in nightly builds.
You can check out the github issues and test cases for further understanding of what's given below
To enable this feature, you need to add the line #![feature(macro_metavar_expr)] to the top of the crate's root module (usually main.rs or lib.rs), and also set your repo to use nightly builds, which is easily done by creating a file rust-toolchain.toml in the root directory (alongside Cargo.toml) and add the folllowing lines to it:
[toolchain]
channel = "nightly"
Now, instead of providing a solution to you specific problem, I'd like to share a generic solution I created to better illustrate most situations.
I highly recommend studying the code AND the comments, by pasting the following two code blocks in a file (main.rs).
The macro_rules
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
struct SumLen {
sum: i32,
len: u32
}
/// currently one `i32` type is available
///
/// # Examples
///
/// The output of the following:
/// ```ignore
/// sumnarr!(i32 => 5 ; 6, 7, 8)
/// ```
/// will be `[(5, 1), (21, 3)]`
macro_rules! sumnarr {
( $type:ty => $( $( $x: expr ),* );* ) => {
{
// `${count(x,0)}` will give you "length" (number of iterations)
// in `$( )*` loop that you are IMMEDIATELY OUTSIDE OF (e.g.: the `$( )*` loop below)
// `${count(x,1)}` will give you TOTAL number of iterations that the `$( )*` loop
// INSIDE of the IMMEDIATE `$( )*` loop will make. i.e. it is similar to converting
// [ [i1,i2], [i1,i2,i3] ] TO [ i1,i2,i3,i4,i5 ] i.e. flatten the nested iteration.
// In case of `[ [i1,i2], [i1,i2,i3] ]`, `${count(x,0)}` is 2 and `${count(x,1)}` is 5
let mut arr: [SumLen; ${count(x,0)}] = [SumLen{ sum:0, len:0}; ${count(x,0)}];
$(
// `${index()}` refers to the iteration number within the `$( )*` loop
arr[${index()}] = {
let mut sum = 0;
//let mut len = 0;
// THe following will give us length is the loop it is IMMEDIATELY OUTSIDE OF
// (the one just below)
let len = ${count(x,0)};
$(
sum += $x;
// If you were NOT using `$x` somewhere else inside `$( )*`,
// then you should use `${ignore(x)};` to inform the compiler
//You could use the below method, where `${length()}` will give you
//"length" or "number of iterations" in current loop that you are in
// OR
// you could go with my method of `${count(x,0)}` which is explained above
//len = ${length()};
)*
SumLen {
sum,
len
}
};
)*
arr
}
};
}
The #[test] (unit test)
#[test]
fn sumnarr_macro() {
let (a, b, c, d, e) = (4, 5, 6, 9, 10);
let sum_lens = [
SumLen {
sum: a + e,
len: 2
},
SumLen {
sum: b + c + d,
len: 3
}
];
assert_eq!(sum_lens, sumnarr!(i32 => a,e;b,c,d));
}
I hope this helps

SweetJS : Write a macro for a specific library

I'm currently working on a little project which consists about writing macros for Ramda. Here is an example :
let map = macro {
rule { $f $array } => { R.map($f, $array) }
rule { $f } => { R.map($f) }
}
I tried to compile this simple sample of code as a beginning :
var R = require('ramda');
function log (value) {
console.log(value);
}
map log [1, 2, 3];
Because of hygiene, the compiled code looks like this :
var R$759 = require('ramda');
function log$761(value$762) {
console.log(value$762);
}
R.map(log$761)[1, 2, 3];
My problem is that I don't know how to make reference to ramda.
Has anyone tried to write macros for a specific library and encountered this problem ?
At the moment the ways to do it are a little hacky. In the next release when we get ES6 modules this will actually be taken care of automatically for you but until then the best option is to have an initialization macro (this is what ki and contracts.js do). This works by having a shared variable in scope for all of your macros and then having the user first invoke an import macro that does the necessary require:
var r_lib;
macro import {
rule { $name from $path } => {
r_lib = require($path);
}
}
let map = macro {
rule { $f $l } => { r_lib.map($f, $l) }
}
import R from "ramda"
map log [1,2,3]