use std::collections::HashSet;
use std::fmt;
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;
use rlua::prelude::{LuaContext, LuaError, LuaFunction, LuaResult, LuaString, LuaTable, LuaValue};
use rlua::{AnyUserData, FromLua, Lua, UserData, UserDataMethods};
use rustc_interface::interface;
use syntax::ThinVec;
use syntax::ast::{
self, DUMMY_NODE_ID, Expr, ExprKind, Lit, LitIntType, LitKind, MacDelimiter,
NodeId, PathSegment, Ty,
};
use syntax::mut_visit::MutVisitor;
use syntax::token::{Lit as TokenLit, LitKind as TokenLitKind, Nonterminal, Token, TokenKind};
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax::tokenstream::TokenTree;
use syntax_pos::DUMMY_SP;
use c2rust_ast_builder::mk;
use crate::ast_manip::MutVisit;
use crate::ast_manip::fn_edit::{mut_visit_fns, FnLike};
use crate::command::{self, CommandState, RefactorState};
use crate::driver::{self, Phase};
use crate::file_io::{OutputMode, RealFileIO};
use crate::matcher::{self, mut_visit_match_with, replace_expr, MatchCtxt, Pattern, Subst, TryMatch};
use crate::path_edit::fold_resolved_paths_with_id;
use crate::reflect::reflect_tcx_ty;
use crate::{Command, RefactorCtxt};
pub mod ast_visitor;
pub mod into_lua_ast;
pub mod merge_lua_ast;
mod lua_ty;
mod to_lua_ast_node;
use ast_visitor::{LuaAstVisitor, LuaAstVisitorNew};
use lua_ty::LuaTy;
use into_lua_ast::IntoLuaAst;
use merge_lua_ast::MergeLuaAst;
use to_lua_ast_node::LuaAstNode;
use to_lua_ast_node::{FromLuaAstNode, FromLuaExt, FromLuaTable, LuaHirId, ToLuaExt, ToLuaScoped, ToLuaAstNode};
pub fn validate_command(command: &Command) -> bool {
assert_eq!(command.args.len(), 1);
if !Path::new(&command.args[0]).exists() {
error!("No script file found at {}", command.args[0]);
return false;
}
true
}
pub fn run_lua_file(
script_path: &Path,
config: interface::Config,
registry: command::Registry,
rewrite_modes: Vec<OutputMode>,
) -> io::Result<()> {
let mut file = File::open(script_path)?;
let mut script = vec![];
file.read_to_end(&mut script)?;
let io = Arc::new(RealFileIO::new(rewrite_modes));
driver::run_refactoring(config, registry, io, HashSet::new(), |state| {
let lua = unsafe { Lua::new_with_debug() };
lua.context(|lua_ctx| {
let package: LuaTable = lua_ctx.globals().get("package")?;
let mut path: String = package.get("path")?;
let parent_path = script_path.parent().unwrap_or(&Path::new("./"));
path.push_str(";");
path.push_str(parent_path.to_str().expect("Did not find UTF-8 path"));
path.push_str("/?.lua");
package.set("path", path)?;
lua_ctx.globals().set("package", package)?;
lua_ctx.globals().set("DUMMY_NODE_ID",
DUMMY_NODE_ID.to_lua_ext(lua_ctx)?)?;
lua_ctx.globals().set("DUMMY_SP",
DUMMY_SP.to_lua_ext(lua_ctx)?)?;
lua_ctx.scope(|scope| {
let refactor = scope.create_nonstatic_userdata(state)?;
lua_ctx.globals().set("refactor", refactor)?;
let log_error = scope.create_function(|_lua_ctx, string: LuaString| {
error!("{}", string.to_str()?);
Ok(())
})?;
let log_warn = scope.create_function(|_lua_ctx, string: LuaString| {
warn!("{}", string.to_str()?);
Ok(())
})?;
lua_ctx.globals().set("log_error", log_error)?;
lua_ctx.globals().set("log_warn", log_warn)?;
lua_ctx.load(&script).exec()
})
})
})
.unwrap_or_else(|e| panic!("User script failed: {}", DisplayLuaError(e)));
Ok(())
}
fn lua_serialize_marks<'lua>(
marks: &HashSet<(NodeId, Symbol)>,
lua_ctx: LuaContext<'lua>,
) -> LuaResult<LuaTable<'lua>> {
let tbl = lua_ctx.create_table()?;
for (node_id, sym) in marks.iter() {
let set: Option<LuaTable> = tbl.get(node_id.as_usize())?;
let set = set.unwrap_or(lua_ctx.create_table()?);
set.set(&*sym.as_str(), true)?;
tbl.set(node_id.as_usize(), set)?;
}
Ok(tbl)
}
struct DisplayLuaError(LuaError);
impl fmt::Display for DisplayLuaError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.0 {
LuaError::SyntaxError{message, ..} => write!(f, "Syntax error while parsing lua: {}", message),
LuaError::RuntimeError(e) => write!(f, "Runtime error during lua execution: {}", e),
LuaError::CallbackError { traceback, cause } => {
write!(f, "Callback error: {}, traceback: {}", cause, traceback)
}
e => e.fmt(f),
}
}
}
#[allow(unused_doc_comments)]
impl UserData for RefactorState {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method_mut(
"run_command",
|_lua_ctx, this, (name, args): (String, Vec<String>)| {
this.run(&name, &args).map_err(LuaError::external)
},
);
methods.add_method_mut(
"save_crate",
|_lua_ctx, this, ()| Ok(this.save_crate()),
);
methods.add_method_mut(
"load_crate",
|_lua_ctx, this, ()| Ok(this.load_crate()),
);
methods.add_method(
"dump_marks",
|_lua_ctx, this, ()| Ok(println!("Marks: {:?}", this.marks())),
);
methods.add_method_mut(
"clear_marks",
|_lua_ctx, this, ()| Ok(this.clear_marks()),
);
methods.add_method(
"get_marks",
|lua_ctx, this, ()| lua_serialize_marks(&*this.marks(), lua_ctx),
);
methods.add_method_mut("transform", |lua_ctx, this, (callback, phase): (LuaFunction, Option<u8>)| {
let phase = match phase {
Some(1) => Phase::Phase1,
Some(2) => Phase::Phase2,
Some(3) | None => Phase::Phase3,
_ => return Err(LuaError::external("Phase must be nil, 1, 2, or 3")),
};
this.transform_crate(phase, |st, cx| {
enter_transform(st, cx, |transform| {
let res: LuaResult<()> = lua_ctx.scope(|scope| {
let transform_data = scope.create_nonstatic_userdata(transform)?;
callback.call(transform_data)?;
Ok(())
});
res.unwrap_or_else(|e| {
match e {
LuaError::CallbackError { traceback, cause } => {
panic!("Could not run transform due to {:#?} at:\n{}", cause, traceback);
}
_ => panic!("Could not run transform due to {:#?}", e),
}
});
});
})
.map_err(|e| LuaError::external(format!("Failed to run compiler: {:#?}", e)))?;
Ok(())
});
}
}
macro_rules! dispatch {
(
$this: ident.$method: ident,
$node: ident,
$lua_ctx: ident,
$params: tt,
{$($tys: ty),+}
$(,)*
) => {
$(
if let Ok($node) = <LuaAstNode<$tys> as FromLuaAstNode>::from_lua_ast_node($node.clone(), $lua_ctx) {
dispatch!(@call $this.$method(&$node, $params))
} else
)*
{
panic!("Could not match node type from Lua")
}
};
(
$this: ident.$method: ident<$generic: ty>,
$node: ident,
$params: tt,
{$($tys: ty),+}
$(,)*
) => {
$(
if let Ok($node) = <LuaAstNode<$tys> as FromLuaAstNode>::from_lua_ast_node($node.clone(), $lua_ctx) {
dispatch!(@call $this.$method<$generic>(&$node, $params))
} else
)*
{
panic!("Could not match node type from Lua")
}
};
(@call $this:ident.$method:ident ($node: expr, ($($arg:expr),*))) => {
$this.$method($node, $($arg),*)
};
(@call $this:ident.$method:ident<$generic: ty> ($node: expr, ($($arg:expr),*))) => {
$this.$method::<$generic, _>($node, $($arg),*)
};
}
struct ScriptingMatchCtxt<'a, 'tcx: 'a> {
mcx: MatchCtxt<'a, 'tcx>,
transform: TransformCtxt<'a, 'tcx>,
}
impl<'a, 'tcx> ScriptingMatchCtxt<'a, 'tcx> {
fn new(transform: TransformCtxt<'a, 'tcx>) -> Self {
Self {
mcx: MatchCtxt::new(transform.st, transform.cx),
transform,
}
}
fn new_subcontext(&self, mcx: MatchCtxt<'a, 'tcx>) -> Self {
Self {
mcx,
transform: self.transform.clone(),
}
}
fn fold_with<'lua, P, V>(
&mut self,
pattern: &LuaAstNode<P>,
krate: &mut ast::Crate,
callback: LuaFunction<'lua>,
lua_ctx: LuaContext<'lua>,
)
where P: Pattern<V> + Clone,
V: 'static + ToLuaScoped + Clone,
LuaAstNode<P>: 'static + UserData,
LuaAstNode<V>: 'static + UserData,
{
let pattern = pattern.borrow().clone();
mut_visit_match_with(self.mcx.clone(), pattern, krate, |x, mcx| {
let mcx = self.new_subcontext(mcx);
let new_node: LuaAstNode<V> = lua_ctx
.scope(|scope| {
let node = x.clone().to_lua_scoped(lua_ctx, scope)?;
let mcx = scope.create_nonstatic_userdata(mcx)?;
callback.call((node, mcx))
})
.unwrap_or_else(|e| {
panic!("Could not execute callback in match:fold_with {:#?}", e)
});
*x = new_node.borrow().clone();
})
}
fn try_match<'lua, T>(
&mut self,
pat: &LuaAstNode<T>,
target: LuaValue<'lua>,
lua_ctx: LuaContext<'lua>,
) -> LuaResult<bool>
where T: TryMatch + FromLuaTable + Clone,
LuaAstNode<T>: 'static + UserData,
{
let target: LuaAstNode<T> = match FromLuaAstNode::from_lua_ast_node(target, lua_ctx) {
Ok(t) => t,
Err(_) => return Ok(false), };
let res = self.mcx.try_match(&*pat.borrow(), &*target.borrow()).is_ok();
Ok(res)
}
fn find_first<P, T>(&mut self, pat: &LuaAstNode<P>, target: &mut T) -> LuaResult<bool>
where P: Pattern<P> + Clone,
LuaAstNode<P>: 'static + UserData,
T: MutVisit,
{
let res = matcher::find_first_with(self.mcx.clone(), pat.borrow().clone(), target).is_some();
Ok(res)
}
fn subst<'lua, T>(&self, node: &LuaAstNode<T>, lua_ctx: LuaContext<'lua>) -> LuaResult<LuaValue<'lua>>
where T: Subst + Clone + ToLuaAstNode,
{
node.borrow()
.clone()
.subst(self.transform.st, self.transform.cx, &self.mcx.bindings)
.to_lua_ast_node(lua_ctx)
}
}
#[allow(unused_doc_comments)]
impl<'a, 'tcx> UserData for ScriptingMatchCtxt<'a, 'tcx> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method_mut("parse_stmts", |lua_ctx, this, pat: String| {
let stmts = this.mcx.parse_stmts(&pat);
stmts.to_lua_ast_node(lua_ctx)
});
methods.add_method_mut("parse_expr", |lua_ctx, this, pat: String| {
let expr = this.mcx.parse_expr(&pat);
expr.to_lua_ext(lua_ctx)
});
methods.add_method_mut(
"fold_with",
|lua_ctx, this, (needle, callback): (LuaValue, LuaFunction)| {
this.transform.st.map_krate(|krate| {
dispatch!(
this.fold_with,
needle,
lua_ctx,
(krate, callback, lua_ctx),
{P<ast::Expr>, P<ast::Ty>, Vec<ast::Stmt>},
)
});
Ok(())
},
);
methods.add_method_mut("get_lit", |lua_ctx, this, pattern: String| {
this.mcx.bindings
.get::<_, ast::Lit>(pattern)
.unwrap()
.clone()
.to_lua_ext(lua_ctx)
});
methods.add_method_mut("get_expr", |lua_ctx, this, pattern: String| {
this.mcx.bindings
.get::<_, P<ast::Expr>>(pattern)
.unwrap()
.clone()
.to_lua_ext(lua_ctx)
});
methods.add_method_mut("get_ty", |lua_ctx, this, pattern: String| {
this.mcx.bindings
.get::<_, P<ast::Ty>>(pattern)
.unwrap()
.clone()
.to_lua_ext(lua_ctx)
});
methods.add_method_mut("get_stmt", |lua_ctx, this, pattern: String| {
this.mcx.bindings.get::<_, ast::Stmt>(pattern).to_lua_ext(lua_ctx)
});
methods.add_method_mut("get_multistmt", |lua_ctx, this, pattern: String| {
this.mcx.bindings.get::<_, Vec<ast::Stmt>>(pattern)
.cloned()
.map(|v| v.to_lua_ast_node(lua_ctx))
.unwrap_or(Ok(LuaValue::Nil))
});
methods.add_method_mut(
"try_match",
|lua_ctx, this, (pat, target): (LuaValue, LuaValue)| {
dispatch!(this.try_match, pat, lua_ctx, (target, lua_ctx), {P<ast::Expr>})
},
);
methods.add_method_mut(
"find_first",
|lua_ctx, this, (pat, target): (LuaValue, AnyUserData)| {
if let Ok(t) = target.borrow::<LuaAstNode<Vec<ast::Stmt>>>() {
dispatch!(this.find_first, pat, lua_ctx, (&mut *t.borrow_mut()), {P<ast::Expr>})
} else {
Err(LuaError::external("Only MultiStmt targets are supported for now"))
}
},
);
methods.add_method_mut("subst", |lua_ctx, this, replacement: LuaValue| {
dispatch!(this.subst, replacement, lua_ctx, (lua_ctx), {P<ast::Expr>, Vec<ast::Stmt>, ast::Stmt})
});
}
}
#[derive(Clone)]
pub(crate) struct TransformCtxt<'a, 'tcx: 'a> {
st: &'a CommandState,
cx: &'a RefactorCtxt<'a, 'tcx>,
}
fn enter_transform<'a, 'tcx, F, R>(st: &'a CommandState, cx: &'a RefactorCtxt<'a, 'tcx>, f: F) -> R
where F: Fn(TransformCtxt) -> R
{
let ctx = TransformCtxt {
st,
cx,
};
f(ctx)
}
impl<'a, 'tcx> TransformCtxt<'a, 'tcx> {
fn fold_paths<'lua, T>(&self, node: &LuaAstNode<T>, callback: LuaFunction<'lua>, lua_ctx: LuaContext<'lua>) -> LuaResult<()>
where T: MutVisit,
LuaAstNode<T>: 'static + UserData + Clone,
{
node.map(|node| {
fold_resolved_paths_with_id(node, self.cx, |id, qself, path, defs| {
let (qself, path): (Option<LuaAstNode<ast::QSelf>>, LuaAstNode<ast::Path>) = lua_ctx
.scope(|scope| {
let qself = qself.map(|q| q.to_lua_scoped(lua_ctx, scope).unwrap());
let path = path.to_lua_scoped(lua_ctx, scope).unwrap();
let defs: Vec<_> = defs.into_iter().map(|def| def.to_lua_scoped(lua_ctx, scope).unwrap()).collect();
callback.call((
id.to_lua_ext(lua_ctx).unwrap(),
qself,
path,
defs,
))
})
.unwrap_or_else(|e| panic!("Lua callback failed in visit_paths: {}", DisplayLuaError(e)));
(qself.map(|x| x.into_inner()), path.into_inner())
});
});
Ok(())
}
fn create_use_tree<'lua>(
&self,
lua_ctx: LuaContext<'lua>,
lua_tree: LuaTable<'lua>,
ident: Option<ast::Ident>
) -> LuaResult<ast::UseTree> {
let mut trees: Vec<ast::UseTree> = vec![];
for pair in lua_tree.pairs::<LuaValue, LuaValue>() {
let (key, value) = pair?;
match key {
LuaValue::Integer(_) => {
let ident_str = LuaString::from_lua(value, lua_ctx)?;
trees.push(mk().use_tree(
ident_str.to_str()?,
ast::UseTreeKind::Simple(None, DUMMY_NODE_ID, DUMMY_NODE_ID),
));
}
LuaValue::String(s) => match value {
LuaValue::String(ref glob) if glob.to_str()? == "*" => {
trees.push(mk().use_tree(s.to_str()?, ast::UseTreeKind::Glob));
}
LuaValue::Table(items) => {
trees.push(self.create_use_tree(
lua_ctx,
items,
Some(ast::Ident::from_str(s.to_str()?)),
)?);
}
_ => return Err(LuaError::FromLuaConversionError {
from: "UseTree table value",
to: "ast::UseTree",
message: Some("UseTree table keys must be \"*\" or a table".to_string()),
})
},
_ => return Err(LuaError::FromLuaConversionError {
from: "UseTree table key",
to: "ast::UseTree",
message: Some("UseTree table keys must be integer or string".to_string()),
}),
}
}
if trees.len() == 1 {
let mut use_tree = trees.pop().unwrap();
if let Some(ident) = ident {
use_tree.prefix.segments.insert(0, PathSegment::from_ident(ident));
}
return Ok(use_tree);
}
let prefix = ast::Path::from_ident(ident.unwrap_or_else(|| ast::Ident::from_str("")));
Ok(mk().use_tree(prefix, ast::UseTreeKind::Nested(
trees.into_iter().map(|u| (u, DUMMY_NODE_ID)).collect()
)))
}
}
#[allow(unused_doc_comments)]
impl<'a, 'tcx> UserData for TransformCtxt<'a, 'tcx> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method(
"dump_marks",
|_lua_ctx, this, ()| Ok(println!("Marks: {:?}", this.st.marks())),
);
methods.add_method_mut(
"clear_marks",
|_lua_ctx, this, ()| Ok(this.st.marks_mut().clear()),
);
methods.add_method(
"get_marks",
|lua_ctx, this, ()| lua_serialize_marks(&*this.st.marks(), lua_ctx),
);
methods.add_method(
"dump_crate",
|_lua_ctx, this, ()| Ok(println!("{:#?}", this.st.krate())),
);
methods.add_method(
"replace_stmts_with",
|lua_ctx, this, (pat, callback): (String, LuaFunction)| {
this.st.map_krate(|krate| {
let mut smcx = ScriptingMatchCtxt::new(this.clone());
let pat = smcx.mcx.parse_stmts(&pat);
smcx.fold_with(&LuaAstNode::new(pat), krate, callback, lua_ctx);
Ok(())
})
},
);
methods.add_method(
"resolve_path_hirid",
|_lua_ctx, this, expr: LuaAstNode<P<Expr>>| {
Ok(this.cx.try_resolve_expr_to_hid(&expr.borrow()).map(LuaHirId))
},
);
methods.add_method("resolve_ty_hirid", |_lua_ctx, this, ty: LuaAstNode<P<Ty>>| {
let def_id = match this.cx.try_resolve_ty(&ty.borrow()) {
Some(id) => id,
None => return Ok(None),
};
Ok(this.cx.hir_map().as_local_hir_id(def_id).map(LuaHirId))
});
methods.add_method(
"hirid_to_nodeid",
|_lua_ctx, this, id: LuaHirId| {
Ok(this.cx.hir_map().hir_to_node_id(id.0).as_usize())
},
);
methods.add_method(
"nodeid_to_hirid",
|_lua_ctx, this, id: i64| {
let node_id = NodeId::from_usize(id as usize);
Ok(LuaHirId(this.cx.hir_map().node_to_hir_id(node_id)))
},
);
methods.add_method(
"replace_expr",
|_lua_ctx, this, (needle, haystack): (String, String)| {
this.st.map_krate(|krate| {
replace_expr(this.st, this.cx, krate, &needle, &haystack);
Ok(())
})
},
);
methods.add_method(
"replace_expr_with",
|lua_ctx, this, (needle, callback): (String, LuaFunction)| {
this.st.map_krate(|krate| {
let mut smcx = ScriptingMatchCtxt::new(this.clone());
let needle = smcx.mcx.parse_expr(&needle);
smcx.fold_with(&LuaAstNode::new(needle), krate, callback, lua_ctx);
Ok(())
})
},
);
methods.add_method("match", |lua_ctx, this, callback: LuaFunction| {
let init_mcx = ScriptingMatchCtxt::new(this.clone());
lua_ctx.scope(|scope| {
let init_mcx = scope.create_nonstatic_userdata(init_mcx)?;
callback.call::<_, ()>(init_mcx)
})
});
methods.add_method(
"visit_crate",
|lua_ctx, this, visitor_obj: LuaTable| {
this.st.map_krate(|krate| {
let lua_crate = krate.clone().into_lua_ast(this, lua_ctx)?;
let visitor = LuaAstVisitor::new(visitor_obj);
visitor.visit_crate(lua_crate.clone())?;
visitor.finish()?;
krate.merge_lua_ast(lua_crate)
})
},
);
methods.add_method(
"visit_crate_new",
|lua_ctx, this, visitor_obj: LuaTable| {
this.st.map_krate(|krate: &mut ast::Crate| {
let mut visitor = LuaAstVisitorNew::new(lua_ctx, visitor_obj);
visitor.visit_crate(krate);
Ok(())
})
},
);
methods.add_method(
"visit_fn_like",
|lua_ctx, this, visitor_obj: LuaTable| {
this.st.map_krate(|krate| {
let mut result = Ok(());
let visitor = LuaAstVisitor::new(visitor_obj);
let wrapper = |fn_like: &mut FnLike| -> LuaResult<()> {
let lua_fn_like = fn_like.clone().into_lua_ast(this, lua_ctx)?;
visitor.visit_fn_like(lua_fn_like.clone())?;
visitor.finish()?;
fn_like.merge_lua_ast(lua_fn_like)
};
mut_visit_fns(krate, |mut fn_like| {
if result.is_err() {
return;
}
match wrapper(&mut fn_like) {
Ok(()) => (),
Err(e) => {
result = Err(e);
}
};
});
result
})
},
);
methods.add_method("visit_paths", |lua_ctx, this, (node, callback): (LuaValue<'lua>, LuaFunction<'lua>)| {
dispatch!(this.fold_paths, node, lua_ctx, (callback, lua_ctx), {P<ast::Item>})
});
methods.add_method("create_use", |lua_ctx, this, (tree, pub_vis): (LuaTable, bool)| {
let use_tree = this.create_use_tree(lua_ctx, tree, None)?;
let mut builder = mk();
if pub_vis { builder = builder.pub_(); }
Ok(builder.use_item(use_tree).to_lua_ext(lua_ctx))
});
methods.add_method("get_use_def", |lua_ctx, this, id: u32| {
this.cx.resolve_use_id(ast::NodeId::from_u32(id)).res.to_lua_ext(lua_ctx)
});
methods.add_method("vec_mac_init_num", |lua_ctx, _this, (init, num): (LuaValue, LuaValue)| {
let init = Rc::new(Nonterminal::NtExpr(FromLuaExt::from_lua_ext(init, lua_ctx)?));
let num = Rc::new(Nonterminal::NtExpr(FromLuaExt::from_lua_ext(num, lua_ctx)?));
let macro_body = vec![
TokenTree::Token(Token { kind: TokenKind::Interpolated(init), span: DUMMY_SP }),
TokenTree::Token(Token { kind: TokenKind::Semi, span: DUMMY_SP }),
TokenTree::Token(Token { kind: TokenKind::Interpolated(num), span: DUMMY_SP}),
];
let mac = mk().mac(mk().path("vec"), macro_body, MacDelimiter::Bracket);
let expr = P(Expr {
id: DUMMY_NODE_ID,
kind: ExprKind::Mac(mac),
span: DUMMY_SP,
attrs: ThinVec::new(),
});
Ok(LuaAstNode::new(expr))
});
methods.add_method("int_lit_expr", |_lua_ctx, _this, (int, _suffix): (i64, Option<LuaString>)| {
let lit = Lit {
token: TokenLit {
kind: TokenLitKind::Integer,
symbol: Symbol::intern(&format!("{}", int)),
suffix: None,
},
kind: LitKind::Int(int as u128, LitIntType::Unsuffixed),
span: DUMMY_SP,
};
let expr = P(Expr {
id: DUMMY_NODE_ID,
kind: ExprKind::Lit(lit),
span: DUMMY_SP,
attrs: ThinVec::new(),
});
Ok(LuaAstNode::new(expr))
});
methods.add_method("get_expr_ty", |_lua_ctx, this, expr: LuaAstNode<P<Expr>>| {
let rty = this.cx.node_type(expr.borrow().id);
let ty = reflect_tcx_ty(this.cx.ty_ctxt(), rty);
Ok(LuaAstNode::new(ty))
});
methods.add_method("get_field_expr_hirid", |_lua_ctx, this, expr: LuaAstNode<P<Expr>>| {
use rustc::ty::TyKind;
let expr = expr.borrow();
let (ref expr, ref ident) = match &expr.kind {
ExprKind::Field(expr, ident) => (expr, ident),
_ => return Ok(None)
};
if let TyKind::Adt(def, _) = &this.cx.adjusted_node_type(expr.id).kind {
let def = def.variants.iter().next().unwrap();
for field in def.fields.iter() {
if field.ident == **ident {
let hir_id = this.cx.hir_map().as_local_hir_id(field.did);
return Ok(hir_id.map(LuaHirId));
}
}
};
Ok(None)
});
methods.add_method("get_node_type", |lua_ctx, this, (node_id,): (LuaValue,)| {
let node_id: NodeId = FromLuaExt::from_lua_ext(node_id, lua_ctx)?;
this.cx
.opt_node_type(node_id)
.map(|ty| LuaTy::from_ty(ty, this.cx))
.map(|lua_ty| lua_ctx.create_userdata(lua_ty))
.transpose()
})
}
}