fix: don't warn for unused control flow result values

This commit is contained in:
Shunsuke Shibayama 2025-02-08 16:44:39 +09:00
parent 21582953c0
commit 38d9ff06ee
7 changed files with 142 additions and 9 deletions

View file

@ -1886,7 +1886,7 @@ impl Context {
{
log!(info "~> {after}\n");
*self_t = *after.clone();
if let hir::Expr::Accessor(hir::Accessor::Ident(ident)) = receiver {
if let Some(ident) = receiver.as_ident() {
if let Some(vi) = self.rec_get_mut_var_info(&ident.raw, AccessKind::Name) {
vi.t = self_t.clone();
}
@ -2783,7 +2783,7 @@ impl Context {
input: &Input,
namespace: &Context,
) -> FailableOption<VarInfo> {
if let hir::Expr::Accessor(hir::Accessor::Ident(local)) = obj {
if let Some(local) = obj.as_ident() {
if local.vis().is_private() {
match &local.inspect()[..] {
"match" => {

View file

@ -1589,7 +1589,7 @@ impl Context {
let arg_ts = ctx.params.iter().map(|(_, vi)| &vi.t);
for ((tp, arg), arg_t) in ctx.typ.typarams().iter().zip(args.pos_args()).zip(arg_ts) {
let tp = self.detach_tp(tp.clone(), &mut tv_ctx);
if let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = &arg.expr {
if let Some(ident) = &arg.expr.as_ident() {
if self.subtype_of(arg_t, &Type::Type) {
if let Ok(tv) = self.convert_tp_into_type(tp.clone()) {
let _ = tv_ctx.push_or_init_tyvar(&ident.name, &tv, self);

View file

@ -1320,9 +1320,7 @@ impl Context {
}
}
ClassAttr::Decl(decl) => {
if let ast::Expr::Accessor(ast::Accessor::Ident(ident)) =
decl.expr.as_ref()
{
if let Some(ident) = decl.expr.as_ident() {
if let Err((_, errs)) =
self.declare_var(ident, &decl.t_spec)
{
@ -1362,7 +1360,7 @@ impl Context {
if !self.kind.is_module() {
continue;
}
if let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = tasc.expr.as_ref() {
if let Some(ident) = tasc.expr.as_ident() {
if let Err((_, errs)) = self.declare_var(ident, &tasc.t_spec) {
total_errs.extend(errs);
}

View file

@ -658,6 +658,25 @@ impl_display_from_nested!(Accessor);
impl_locational_for_enum!(Accessor; Ident, Attr);
impl_t_for_enum!(Accessor; Ident, Attr);
impl<'x> TryFrom<&'x Accessor> for &'x Identifier {
type Error = ();
fn try_from(acc: &'x Accessor) -> Result<Self, ()> {
match acc {
Accessor::Ident(ident) => Ok(ident),
_ => Err(()),
}
}
}
impl<'x> TryFrom<&'x Accessor> for &'x Attribute {
type Error = ();
fn try_from(acc: &'x Accessor) -> Result<Self, ()> {
match acc {
Accessor::Attr(attr) => Ok(attr),
_ => Err(()),
}
}
}
impl Accessor {
pub fn private_with_line(name: Str, line: u32) -> Self {
Self::Ident(Identifier::private_with_line(name, line))
@ -772,6 +791,20 @@ impl Accessor {
None => None,
}
}
pub fn as_ident(&self) -> Option<&Identifier> {
match self {
Self::Ident(ident) => Some(ident),
_ => None,
}
}
pub fn as_attr(&self) -> Option<&Attribute> {
match self {
Self::Attr(attr) => Some(attr),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -3016,6 +3049,46 @@ impl Expr {
}
}
pub fn as_call(&self) -> Option<&Call> {
<&Call>::try_from(self).ok()
}
pub fn as_binop(&self) -> Option<&BinOp> {
<&BinOp>::try_from(self).ok()
}
pub fn as_unaryop(&self) -> Option<&UnaryOp> {
<&UnaryOp>::try_from(self).ok()
}
pub fn as_def(&self) -> Option<&Def> {
<&Def>::try_from(self).ok()
}
pub fn as_lambda(&self) -> Option<&Lambda> {
<&Lambda>::try_from(self).ok()
}
pub fn as_class_def(&self) -> Option<&ClassDef> {
<&ClassDef>::try_from(self).ok()
}
pub fn as_literal(&self) -> Option<&Literal> {
<&Literal>::try_from(self).ok()
}
pub fn as_accessor(&self) -> Option<&Accessor> {
<&Accessor>::try_from(self).ok()
}
pub fn as_ident(&self) -> Option<&Identifier> {
self.as_accessor().and_then(|acc| acc.as_ident())
}
pub fn as_attr(&self) -> Option<&Attribute> {
self.as_accessor().and_then(|acc| acc.as_attr())
}
pub fn show_acc(&self) -> Option<String> {
match self {
Expr::Accessor(acc) => Some(acc.show()),

View file

@ -2,6 +2,7 @@
//! What is implemented here affects subsequent optimizations,
//! and `erg_linter` does linting that does not affect optimizations.
use erg_common::consts::PYTHON_MODE;
#[allow(unused_imports)]
use erg_common::log;
use erg_common::pathutil::NormalizedPathBuf;
@ -68,6 +69,13 @@ impl<ASTBuilder: ASTBuildable> GenericASTLowerer<ASTBuilder> {
/// OK: exec `None`
fn expr_use_check(&self, expr: &hir::Expr) -> LowerResult<()> {
if !expr.ref_t().is_nonelike() && !expr.is_type_asc() && !expr.is_doc_comment() {
if PYTHON_MODE
&& expr
.as_call()
.is_some_and(|call| call.control_kind().is_some())
{
return self.block_use_check(expr);
}
if expr.ref_t().is_subr() {
Err(LowerWarnings::from(
LowerWarning::unused_subroutine_warning(

View file

@ -3074,7 +3074,7 @@ impl<A: ASTBuildable> GenericASTLowerer<A> {
if let Some(attr_t) = attr.ref_mut_t() {
*attr_t = sup.clone();
}
if let hir::Accessor::Ident(ident) = &attr {
if let Some(ident) = attr.as_ident() {
if let Some(vi) = self
.module
.context

View file

@ -14,7 +14,7 @@ use erg_common::{
fmt_option, fmt_vec, impl_display_for_enum, impl_display_from_nested,
impl_displayable_stream_for_wrapper, impl_from_trait_for_enum, impl_locational,
impl_locational_for_enum, impl_nested_display_for_chunk_enum, impl_nested_display_for_enum,
impl_stream, impl_traversable_for_enum,
impl_stream, impl_traversable_for_enum, impl_try_from_trait_for_enum,
};
use erg_common::{fmt_vec_split_with, Str};
@ -1019,6 +1019,20 @@ impl Accessor {
Self::TypeApp(app) => app.obj.is_const_acc(),
}
}
pub fn as_ident(&self) -> Option<&Identifier> {
match self {
Self::Ident(ident) => Some(ident),
_ => None,
}
}
pub fn as_attr(&self) -> Option<&Attribute> {
match self {
Self::Attr(attr) => Some(attr),
_ => None,
}
}
}
#[pyclass(get_all, set_all)]
@ -2181,6 +2195,17 @@ impl TryFrom<Expr> for Call {
}
}
}
impl<'x> TryFrom<&'x Expr> for &'x Call {
type Error = ();
fn try_from(expr: &'x Expr) -> Result<Self, Self::Error> {
match expr {
Expr::Call(call) => Ok(call),
Expr::TypeAscription(tasc) => Self::try_from(&*tasc.expr),
Expr::Accessor(Accessor::TypeApp(tapp)) => Self::try_from(&*tapp.obj),
_ => Err(()),
}
}
}
impl_display_from_nested!(Call);
@ -6820,6 +6845,7 @@ pub enum Expr {
impl_nested_display_for_chunk_enum!(Expr; Literal, Accessor, List, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Compound, InlineModule, Dummy);
impl_from_trait_for_enum!(Expr; Literal, Accessor, List, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Compound, InlineModule, Dummy);
impl_try_from_trait_for_enum!(Expr; Literal, Accessor, List, Tuple, Dict, Set, Record, BinOp, UnaryOp, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Compound, InlineModule, Dummy);
impl_display_from_nested!(Expr);
impl_locational_for_enum!(Expr; Literal, Accessor, List, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Compound, InlineModule, Dummy);
impl_into_py_for_enum!(Expr; Literal, Accessor, List, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Compound, InlineModule, Dummy);
@ -7072,6 +7098,34 @@ impl Expr {
_ => 5,
}
}
pub fn as_call(&self) -> Option<&Call> {
<&Call>::try_from(self).ok()
}
pub fn as_def(&self) -> Option<&Def> {
<&Def>::try_from(self).ok()
}
pub fn as_class_def(&self) -> Option<&ClassDef> {
<&ClassDef>::try_from(self).ok()
}
pub fn as_lambda(&self) -> Option<&Lambda> {
<&Lambda>::try_from(self).ok()
}
pub fn as_accessor(&self) -> Option<&Accessor> {
<&Accessor>::try_from(self).ok()
}
pub fn as_ident(&self) -> Option<&Identifier> {
self.as_accessor().and_then(|acc| acc.as_ident())
}
pub fn as_attr(&self) -> Option<&Attribute> {
self.as_accessor().and_then(|acc| acc.as_attr())
}
}
#[pyclass]