mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:25:17 +00:00
Separate type check diagnostics builder (#13978)
## Summary This PR creates a new `TypeCheckDiagnosticsBuilder` for the `TypeCheckDiagnostics` struct. The main motivation behind this is to separate the helpers required to build the diagnostics from the type inference builder itself. This allows us to use such helpers outside of the inference builder like for example in the unpacking logic in https://github.com/astral-sh/ruff/pull/13979. ## Test Plan `cargo insta test`
This commit is contained in:
parent
eddc8d7644
commit
bf20061268
3 changed files with 229 additions and 186 deletions
|
@ -1,4 +1,3 @@
|
||||||
use infer::TypeInferenceBuilder;
|
|
||||||
use ruff_db::files::File;
|
use ruff_db::files::File;
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
|
|
||||||
|
@ -13,6 +12,7 @@ use crate::semantic_index::{
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
builtins_symbol_ty, types_symbol_ty, typeshed_symbol_ty, typing_extensions_symbol_ty,
|
builtins_symbol_ty, types_symbol_ty, typeshed_symbol_ty, typing_extensions_symbol_ty,
|
||||||
};
|
};
|
||||||
|
use crate::types::diagnostic::TypeCheckDiagnosticsBuilder;
|
||||||
use crate::types::narrow::narrowing_constraint;
|
use crate::types::narrow::narrowing_constraint;
|
||||||
use crate::{Db, FxOrderSet, HasTy, Module, SemanticModel};
|
use crate::{Db, FxOrderSet, HasTy, Module, SemanticModel};
|
||||||
|
|
||||||
|
@ -1456,15 +1456,15 @@ impl<'db> CallOutcome<'db> {
|
||||||
&self,
|
&self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
node: ast::AnyNodeRef,
|
node: ast::AnyNodeRef,
|
||||||
builder: &'a mut TypeInferenceBuilder<'db>,
|
diagnostics: &'a mut TypeCheckDiagnosticsBuilder<'db>,
|
||||||
) -> Type<'db> {
|
) -> Type<'db> {
|
||||||
match self.return_ty_result(db, node, builder) {
|
match self.return_ty_result(db, node, diagnostics) {
|
||||||
Ok(return_ty) => return_ty,
|
Ok(return_ty) => return_ty,
|
||||||
Err(NotCallableError::Type {
|
Err(NotCallableError::Type {
|
||||||
not_callable_ty,
|
not_callable_ty,
|
||||||
return_ty,
|
return_ty,
|
||||||
}) => {
|
}) => {
|
||||||
builder.add_diagnostic(
|
diagnostics.add(
|
||||||
node,
|
node,
|
||||||
"call-non-callable",
|
"call-non-callable",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -1479,7 +1479,7 @@ impl<'db> CallOutcome<'db> {
|
||||||
called_ty,
|
called_ty,
|
||||||
return_ty,
|
return_ty,
|
||||||
}) => {
|
}) => {
|
||||||
builder.add_diagnostic(
|
diagnostics.add(
|
||||||
node,
|
node,
|
||||||
"call-non-callable",
|
"call-non-callable",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -1495,7 +1495,7 @@ impl<'db> CallOutcome<'db> {
|
||||||
called_ty,
|
called_ty,
|
||||||
return_ty,
|
return_ty,
|
||||||
}) => {
|
}) => {
|
||||||
builder.add_diagnostic(
|
diagnostics.add(
|
||||||
node,
|
node,
|
||||||
"call-non-callable",
|
"call-non-callable",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -1514,7 +1514,7 @@ impl<'db> CallOutcome<'db> {
|
||||||
&self,
|
&self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
node: ast::AnyNodeRef,
|
node: ast::AnyNodeRef,
|
||||||
builder: &'a mut TypeInferenceBuilder<'db>,
|
diagnostics: &'a mut TypeCheckDiagnosticsBuilder<'db>,
|
||||||
) -> Result<Type<'db>, NotCallableError<'db>> {
|
) -> Result<Type<'db>, NotCallableError<'db>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Callable { return_ty } => Ok(*return_ty),
|
Self::Callable { return_ty } => Ok(*return_ty),
|
||||||
|
@ -1522,7 +1522,7 @@ impl<'db> CallOutcome<'db> {
|
||||||
return_ty,
|
return_ty,
|
||||||
revealed_ty,
|
revealed_ty,
|
||||||
} => {
|
} => {
|
||||||
builder.add_diagnostic(
|
diagnostics.add(
|
||||||
node,
|
node,
|
||||||
"revealed-type",
|
"revealed-type",
|
||||||
format_args!("Revealed type is `{}`", revealed_ty.display(db)),
|
format_args!("Revealed type is `{}`", revealed_ty.display(db)),
|
||||||
|
@ -1554,10 +1554,10 @@ impl<'db> CallOutcome<'db> {
|
||||||
*return_ty
|
*return_ty
|
||||||
} else {
|
} else {
|
||||||
revealed = true;
|
revealed = true;
|
||||||
outcome.unwrap_with_diagnostic(db, node, builder)
|
outcome.unwrap_with_diagnostic(db, node, diagnostics)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => outcome.unwrap_with_diagnostic(db, node, builder),
|
_ => outcome.unwrap_with_diagnostic(db, node, diagnostics),
|
||||||
};
|
};
|
||||||
union_builder = union_builder.add(return_ty);
|
union_builder = union_builder.add(return_ty);
|
||||||
}
|
}
|
||||||
|
@ -1640,12 +1640,12 @@ impl<'db> IterationOutcome<'db> {
|
||||||
fn unwrap_with_diagnostic(
|
fn unwrap_with_diagnostic(
|
||||||
self,
|
self,
|
||||||
iterable_node: ast::AnyNodeRef,
|
iterable_node: ast::AnyNodeRef,
|
||||||
inference_builder: &mut TypeInferenceBuilder<'db>,
|
diagnostics: &mut TypeCheckDiagnosticsBuilder<'db>,
|
||||||
) -> Type<'db> {
|
) -> Type<'db> {
|
||||||
match self {
|
match self {
|
||||||
Self::Iterable { element_ty } => element_ty,
|
Self::Iterable { element_ty } => element_ty,
|
||||||
Self::NotIterable { not_iterable_ty } => {
|
Self::NotIterable { not_iterable_ty } => {
|
||||||
inference_builder.not_iterable_diagnostic(iterable_node, not_iterable_ty);
|
diagnostics.add_not_iterable(iterable_node, not_iterable_ty);
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
use ruff_db::files::File;
|
use ruff_db::files::File;
|
||||||
|
use ruff_python_ast::AnyNodeRef;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::types::Type;
|
||||||
|
use crate::Db;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct TypeCheckDiagnostic {
|
pub struct TypeCheckDiagnostic {
|
||||||
// TODO: Don't use string keys for rules
|
// TODO: Don't use string keys for rules
|
||||||
|
@ -109,3 +113,154 @@ impl<'a> IntoIterator for &'a TypeCheckDiagnostics {
|
||||||
self.inner.iter()
|
self.inner.iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) struct TypeCheckDiagnosticsBuilder<'db> {
|
||||||
|
db: &'db dyn Db,
|
||||||
|
file: File,
|
||||||
|
diagnostics: TypeCheckDiagnostics,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> TypeCheckDiagnosticsBuilder<'db> {
|
||||||
|
pub(super) fn new(db: &'db dyn Db, file: File) -> Self {
|
||||||
|
Self {
|
||||||
|
db,
|
||||||
|
file,
|
||||||
|
diagnostics: TypeCheckDiagnostics::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit a diagnostic declaring that the object represented by `node` is not iterable
|
||||||
|
pub(super) fn add_not_iterable(&mut self, node: AnyNodeRef, not_iterable_ty: Type<'db>) {
|
||||||
|
self.add(
|
||||||
|
node,
|
||||||
|
"not-iterable",
|
||||||
|
format_args!(
|
||||||
|
"Object of type `{}` is not iterable",
|
||||||
|
not_iterable_ty.display(self.db)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit a diagnostic declaring that an index is out of bounds for a tuple.
|
||||||
|
pub(super) fn add_index_out_of_bounds(
|
||||||
|
&mut self,
|
||||||
|
kind: &'static str,
|
||||||
|
node: AnyNodeRef,
|
||||||
|
tuple_ty: Type<'db>,
|
||||||
|
length: usize,
|
||||||
|
index: i64,
|
||||||
|
) {
|
||||||
|
self.add(
|
||||||
|
node,
|
||||||
|
"index-out-of-bounds",
|
||||||
|
format_args!(
|
||||||
|
"Index {index} is out of bounds for {kind} `{}` with length {length}",
|
||||||
|
tuple_ty.display(self.db)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit a diagnostic declaring that a type does not support subscripting.
|
||||||
|
pub(super) fn add_non_subscriptable(
|
||||||
|
&mut self,
|
||||||
|
node: AnyNodeRef,
|
||||||
|
non_subscriptable_ty: Type<'db>,
|
||||||
|
method: &str,
|
||||||
|
) {
|
||||||
|
self.add(
|
||||||
|
node,
|
||||||
|
"non-subscriptable",
|
||||||
|
format_args!(
|
||||||
|
"Cannot subscript object of type `{}` with no `{method}` method",
|
||||||
|
non_subscriptable_ty.display(self.db)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_unresolved_module(
|
||||||
|
&mut self,
|
||||||
|
import_node: impl Into<AnyNodeRef<'db>>,
|
||||||
|
level: u32,
|
||||||
|
module: Option<&str>,
|
||||||
|
) {
|
||||||
|
self.add(
|
||||||
|
import_node.into(),
|
||||||
|
"unresolved-import",
|
||||||
|
format_args!(
|
||||||
|
"Cannot resolve import `{}{}`",
|
||||||
|
".".repeat(level as usize),
|
||||||
|
module.unwrap_or_default()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_slice_step_size_zero(&mut self, node: AnyNodeRef) {
|
||||||
|
self.add(
|
||||||
|
node,
|
||||||
|
"zero-stepsize-in-slice",
|
||||||
|
format_args!("Slice step size can not be zero"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_invalid_assignment(
|
||||||
|
&mut self,
|
||||||
|
node: AnyNodeRef,
|
||||||
|
declared_ty: Type<'db>,
|
||||||
|
assigned_ty: Type<'db>,
|
||||||
|
) {
|
||||||
|
match declared_ty {
|
||||||
|
Type::ClassLiteral(class) => {
|
||||||
|
self.add(node, "invalid-assignment", format_args!(
|
||||||
|
"Implicit shadowing of class `{}`; annotate to make it explicit if this is intentional",
|
||||||
|
class.name(self.db)));
|
||||||
|
}
|
||||||
|
Type::FunctionLiteral(function) => {
|
||||||
|
self.add(node, "invalid-assignment", format_args!(
|
||||||
|
"Implicit shadowing of function `{}`; annotate to make it explicit if this is intentional",
|
||||||
|
function.name(self.db)));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.add(
|
||||||
|
node,
|
||||||
|
"invalid-assignment",
|
||||||
|
format_args!(
|
||||||
|
"Object of type `{}` is not assignable to `{}`",
|
||||||
|
assigned_ty.display(self.db),
|
||||||
|
declared_ty.display(self.db),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new diagnostic.
|
||||||
|
///
|
||||||
|
/// The diagnostic does not get added if the rule isn't enabled for this file.
|
||||||
|
pub(super) fn add(&mut self, node: AnyNodeRef, rule: &str, message: std::fmt::Arguments) {
|
||||||
|
if !self.db.is_file_open(self.file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Don't emit the diagnostic if:
|
||||||
|
// * The enclosing node contains any syntax errors
|
||||||
|
// * The rule is disabled for this file. We probably want to introduce a new query that
|
||||||
|
// returns a rule selector for a given file that respects the package's settings,
|
||||||
|
// any global pragma comments in the file, and any per-file-ignores.
|
||||||
|
|
||||||
|
self.diagnostics.push(TypeCheckDiagnostic {
|
||||||
|
file: self.file,
|
||||||
|
rule: rule.to_string(),
|
||||||
|
message: message.to_string(),
|
||||||
|
range: node.range(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn extend(&mut self, diagnostics: &TypeCheckDiagnostics) {
|
||||||
|
self.diagnostics.extend(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn finish(mut self) -> TypeCheckDiagnostics {
|
||||||
|
self.diagnostics.shrink_to_fit();
|
||||||
|
self.diagnostics
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ use ruff_db::files::File;
|
||||||
use ruff_db::parsed::parsed_module;
|
use ruff_db::parsed::parsed_module;
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::{self as ast, AnyNodeRef, Expr, ExprContext, UnaryOp};
|
use ruff_python_ast::{self as ast, AnyNodeRef, Expr, ExprContext, UnaryOp};
|
||||||
use ruff_text_size::Ranged;
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use salsa;
|
use salsa;
|
||||||
use salsa::plumbing::AsId;
|
use salsa::plumbing::AsId;
|
||||||
|
@ -50,7 +49,9 @@ use crate::semantic_index::semantic_index;
|
||||||
use crate::semantic_index::symbol::{NodeWithScopeKind, NodeWithScopeRef, ScopeId};
|
use crate::semantic_index::symbol::{NodeWithScopeKind, NodeWithScopeRef, ScopeId};
|
||||||
use crate::semantic_index::SemanticIndex;
|
use crate::semantic_index::SemanticIndex;
|
||||||
use crate::stdlib::builtins_module_scope;
|
use crate::stdlib::builtins_module_scope;
|
||||||
use crate::types::diagnostic::{TypeCheckDiagnostic, TypeCheckDiagnostics};
|
use crate::types::diagnostic::{
|
||||||
|
TypeCheckDiagnostic, TypeCheckDiagnostics, TypeCheckDiagnosticsBuilder,
|
||||||
|
};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
bindings_ty, builtins_symbol_ty, declarations_ty, global_symbol_ty, symbol_ty,
|
bindings_ty, builtins_symbol_ty, declarations_ty, global_symbol_ty, symbol_ty,
|
||||||
typing_extensions_symbol_ty, BytesLiteralType, ClassType, FunctionType, IterationOutcome,
|
typing_extensions_symbol_ty, BytesLiteralType, ClassType, FunctionType, IterationOutcome,
|
||||||
|
@ -293,6 +294,8 @@ pub(super) struct TypeInferenceBuilder<'db> {
|
||||||
|
|
||||||
/// The type inference results
|
/// The type inference results
|
||||||
types: TypeInference<'db>,
|
types: TypeInference<'db>,
|
||||||
|
|
||||||
|
diagnostics: TypeCheckDiagnosticsBuilder<'db>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> TypeInferenceBuilder<'db> {
|
impl<'db> TypeInferenceBuilder<'db> {
|
||||||
|
@ -322,6 +325,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
region,
|
region,
|
||||||
file,
|
file,
|
||||||
types: TypeInference::empty(scope),
|
types: TypeInference::empty(scope),
|
||||||
|
diagnostics: TypeCheckDiagnosticsBuilder::new(db, file),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +337,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
.declarations
|
.declarations
|
||||||
.extend(inference.declarations.iter());
|
.extend(inference.declarations.iter());
|
||||||
self.types.expressions.extend(inference.expressions.iter());
|
self.types.expressions.extend(inference.expressions.iter());
|
||||||
self.types.diagnostics.extend(&inference.diagnostics);
|
self.diagnostics.extend(&inference.diagnostics);
|
||||||
self.types.has_deferred |= inference.has_deferred;
|
self.types.has_deferred |= inference.has_deferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,37 +509,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_expression(expression.node_ref(self.db));
|
self.infer_expression(expression.node_ref(self.db));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalid_assignment_diagnostic(
|
|
||||||
&mut self,
|
|
||||||
node: AnyNodeRef,
|
|
||||||
declared_ty: Type<'db>,
|
|
||||||
assigned_ty: Type<'db>,
|
|
||||||
) {
|
|
||||||
match declared_ty {
|
|
||||||
Type::ClassLiteral(class) => {
|
|
||||||
self.add_diagnostic(node, "invalid-assignment", format_args!(
|
|
||||||
"Implicit shadowing of class `{}`; annotate to make it explicit if this is intentional",
|
|
||||||
class.name(self.db)));
|
|
||||||
}
|
|
||||||
Type::FunctionLiteral(function) => {
|
|
||||||
self.add_diagnostic(node, "invalid-assignment", format_args!(
|
|
||||||
"Implicit shadowing of function `{}`; annotate to make it explicit if this is intentional",
|
|
||||||
function.name(self.db)));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.add_diagnostic(
|
|
||||||
node,
|
|
||||||
"invalid-assignment",
|
|
||||||
format_args!(
|
|
||||||
"Object of type `{}` is not assignable to `{}`",
|
|
||||||
assigned_ty.display(self.db),
|
|
||||||
declared_ty.display(self.db),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Raise a diagnostic if the given type cannot be divided by zero.
|
/// Raise a diagnostic if the given type cannot be divided by zero.
|
||||||
///
|
///
|
||||||
/// Expects the resolved type of the left side of the binary expression.
|
/// Expects the resolved type of the left side of the binary expression.
|
||||||
|
@ -556,7 +529,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
expr.into(),
|
expr.into(),
|
||||||
"division-by-zero",
|
"division-by-zero",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -581,7 +554,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
// TODO point out the conflicting declarations in the diagnostic?
|
// TODO point out the conflicting declarations in the diagnostic?
|
||||||
let symbol_table = self.index.symbol_table(binding.file_scope(self.db));
|
let symbol_table = self.index.symbol_table(binding.file_scope(self.db));
|
||||||
let symbol_name = symbol_table.symbol(binding.symbol(self.db)).name();
|
let symbol_name = symbol_table.symbol(binding.symbol(self.db)).name();
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
node,
|
node,
|
||||||
"conflicting-declarations",
|
"conflicting-declarations",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -593,7 +566,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if !bound_ty.is_assignable_to(self.db, declared_ty) {
|
if !bound_ty.is_assignable_to(self.db, declared_ty) {
|
||||||
self.invalid_assignment_diagnostic(node, declared_ty, bound_ty);
|
self.diagnostics
|
||||||
|
.add_invalid_assignment(node, declared_ty, bound_ty);
|
||||||
// allow declarations to override inference in case of invalid assignment
|
// allow declarations to override inference in case of invalid assignment
|
||||||
bound_ty = declared_ty;
|
bound_ty = declared_ty;
|
||||||
};
|
};
|
||||||
|
@ -610,7 +584,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let ty = if inferred_ty.is_assignable_to(self.db, ty) {
|
let ty = if inferred_ty.is_assignable_to(self.db, ty) {
|
||||||
ty
|
ty
|
||||||
} else {
|
} else {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
node,
|
node,
|
||||||
"invalid-declaration",
|
"invalid-declaration",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -636,7 +610,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let inferred_ty = if inferred_ty.is_assignable_to(self.db, declared_ty) {
|
let inferred_ty = if inferred_ty.is_assignable_to(self.db, declared_ty) {
|
||||||
inferred_ty
|
inferred_ty
|
||||||
} else {
|
} else {
|
||||||
self.invalid_assignment_diagnostic(node, declared_ty, inferred_ty);
|
self.diagnostics
|
||||||
|
.add_invalid_assignment(node, declared_ty, inferred_ty);
|
||||||
// if the assignment is invalid, fall back to assuming the annotation is correct
|
// if the assignment is invalid, fall back to assuming the annotation is correct
|
||||||
declared_ty
|
declared_ty
|
||||||
};
|
};
|
||||||
|
@ -1319,9 +1294,10 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let value_ty = if value_ty.is_literal_string() {
|
let value_ty = if value_ty.is_literal_string() {
|
||||||
Type::LiteralString
|
Type::LiteralString
|
||||||
} else {
|
} else {
|
||||||
value_ty
|
value_ty.iterate(builder.db).unwrap_with_diagnostic(
|
||||||
.iterate(builder.db)
|
AnyNodeRef::from(target),
|
||||||
.unwrap_with_diagnostic(AnyNodeRef::from(target), builder)
|
&mut builder.diagnostics,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
for element in elts {
|
for element in elts {
|
||||||
if let Some(ty) = inner(builder, element, value_ty, name) {
|
if let Some(ty) = inner(builder, element, value_ty, name) {
|
||||||
|
@ -1434,11 +1410,11 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
return match call.return_ty_result(
|
return match call.return_ty_result(
|
||||||
self.db,
|
self.db,
|
||||||
AnyNodeRef::StmtAugAssign(assignment),
|
AnyNodeRef::StmtAugAssign(assignment),
|
||||||
self,
|
&mut self.diagnostics,
|
||||||
) {
|
) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
assignment.into(),
|
assignment.into(),
|
||||||
"unsupported-operator",
|
"unsupported-operator",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -1458,7 +1434,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
|
|
||||||
self.infer_binary_expression_type(left_ty, right_ty, *op)
|
self.infer_binary_expression_type(left_ty, right_ty, *op)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
assignment.into(),
|
assignment.into(),
|
||||||
"unsupported-operator",
|
"unsupported-operator",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -1506,62 +1482,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_body(orelse);
|
self.infer_body(orelse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emit a diagnostic declaring that the object represented by `node` is not iterable
|
|
||||||
pub(super) fn not_iterable_diagnostic(&mut self, node: AnyNodeRef, not_iterable_ty: Type<'db>) {
|
|
||||||
self.add_diagnostic(
|
|
||||||
node,
|
|
||||||
"not-iterable",
|
|
||||||
format_args!(
|
|
||||||
"Object of type `{}` is not iterable",
|
|
||||||
not_iterable_ty.display(self.db)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Emit a diagnostic declaring that an index is out of bounds for a tuple.
|
|
||||||
pub(super) fn index_out_of_bounds_diagnostic(
|
|
||||||
&mut self,
|
|
||||||
kind: &'static str,
|
|
||||||
node: AnyNodeRef,
|
|
||||||
tuple_ty: Type<'db>,
|
|
||||||
length: usize,
|
|
||||||
index: i64,
|
|
||||||
) {
|
|
||||||
self.add_diagnostic(
|
|
||||||
node,
|
|
||||||
"index-out-of-bounds",
|
|
||||||
format_args!(
|
|
||||||
"Index {index} is out of bounds for {kind} `{}` with length {length}",
|
|
||||||
tuple_ty.display(self.db)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn slice_step_size_zero_diagnostic(&mut self, node: AnyNodeRef) {
|
|
||||||
self.add_diagnostic(
|
|
||||||
node,
|
|
||||||
"zero-stepsize-in-slice",
|
|
||||||
format_args!("Slice step size can not be zero"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Emit a diagnostic declaring that a type does not support subscripting.
|
|
||||||
pub(super) fn non_subscriptable_diagnostic(
|
|
||||||
&mut self,
|
|
||||||
node: AnyNodeRef,
|
|
||||||
non_subscriptable_ty: Type<'db>,
|
|
||||||
method: &str,
|
|
||||||
) {
|
|
||||||
self.add_diagnostic(
|
|
||||||
node,
|
|
||||||
"non-subscriptable",
|
|
||||||
format_args!(
|
|
||||||
"Cannot subscript object of type `{}` with no `{method}` method",
|
|
||||||
non_subscriptable_ty.display(self.db)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn infer_for_statement_definition(
|
fn infer_for_statement_definition(
|
||||||
&mut self,
|
&mut self,
|
||||||
target: &ast::ExprName,
|
target: &ast::ExprName,
|
||||||
|
@ -1580,7 +1500,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
} else {
|
} else {
|
||||||
iterable_ty
|
iterable_ty
|
||||||
.iterate(self.db)
|
.iterate(self.db)
|
||||||
.unwrap_with_diagnostic(iterable.into(), self)
|
.unwrap_with_diagnostic(iterable.into(), &mut self.diagnostics)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.types.expressions.insert(
|
self.types.expressions.insert(
|
||||||
|
@ -1622,7 +1542,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
if let Some(module) = self.module_ty_from_name(&module_name) {
|
if let Some(module) = self.module_ty_from_name(&module_name) {
|
||||||
module
|
module
|
||||||
} else {
|
} else {
|
||||||
self.unresolved_module_diagnostic(alias, 0, Some(name));
|
self.diagnostics.add_unresolved_module(alias, 0, Some(name));
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1667,23 +1587,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_optional_expression(cause.as_deref());
|
self.infer_optional_expression(cause.as_deref());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unresolved_module_diagnostic(
|
|
||||||
&mut self,
|
|
||||||
import_node: impl Into<AnyNodeRef<'db>>,
|
|
||||||
level: u32,
|
|
||||||
module: Option<&str>,
|
|
||||||
) {
|
|
||||||
self.add_diagnostic(
|
|
||||||
import_node.into(),
|
|
||||||
"unresolved-import",
|
|
||||||
format_args!(
|
|
||||||
"Cannot resolve import `{}{}`",
|
|
||||||
".".repeat(level as usize),
|
|
||||||
module.unwrap_or_default()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given a `from .foo import bar` relative import, resolve the relative module
|
/// Given a `from .foo import bar` relative import, resolve the relative module
|
||||||
/// we're importing `bar` from into an absolute [`ModuleName`]
|
/// we're importing `bar` from into an absolute [`ModuleName`]
|
||||||
/// using the name of the module we're currently analyzing.
|
/// using the name of the module we're currently analyzing.
|
||||||
|
@ -1767,7 +1670,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let member_ty = module_ty.member(self.db, &ast::name::Name::new(&name.id));
|
let member_ty = module_ty.member(self.db, &ast::name::Name::new(&name.id));
|
||||||
|
|
||||||
if member_ty.is_unbound() {
|
if member_ty.is_unbound() {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
AnyNodeRef::Alias(alias),
|
AnyNodeRef::Alias(alias),
|
||||||
"unresolved-import",
|
"unresolved-import",
|
||||||
format_args!("Module `{module_name}` has no member `{name}`",),
|
format_args!("Module `{module_name}` has no member `{name}`",),
|
||||||
|
@ -1780,7 +1683,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
member_ty.replace_unbound_with(self.db, Type::Never)
|
member_ty.replace_unbound_with(self.db, Type::Never)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.unresolved_module_diagnostic(import_from, *level, module);
|
self.diagnostics
|
||||||
|
.add_unresolved_module(import_from, *level, module);
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1794,7 +1698,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
"Relative module resolution `{}` failed: too many leading dots",
|
"Relative module resolution `{}` failed: too many leading dots",
|
||||||
format_import_from_module(*level, module),
|
format_import_from_module(*level, module),
|
||||||
);
|
);
|
||||||
self.unresolved_module_diagnostic(import_from, *level, module);
|
self.diagnostics
|
||||||
|
.add_unresolved_module(import_from, *level, module);
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
Err(ModuleNameResolutionError::UnknownCurrentModule) => {
|
Err(ModuleNameResolutionError::UnknownCurrentModule) => {
|
||||||
|
@ -1803,7 +1708,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
format_import_from_module(*level, module),
|
format_import_from_module(*level, module),
|
||||||
self.file.path(self.db)
|
self.file.path(self.db)
|
||||||
);
|
);
|
||||||
self.unresolved_module_diagnostic(import_from, *level, module);
|
self.diagnostics
|
||||||
|
.add_unresolved_module(import_from, *level, module);
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2243,7 +2149,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
} else {
|
} else {
|
||||||
iterable_ty
|
iterable_ty
|
||||||
.iterate(self.db)
|
.iterate(self.db)
|
||||||
.unwrap_with_diagnostic(iterable.into(), self)
|
.unwrap_with_diagnostic(iterable.into(), &mut self.diagnostics)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.types
|
self.types
|
||||||
|
@ -2333,7 +2239,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let function_type = self.infer_expression(func);
|
let function_type = self.infer_expression(func);
|
||||||
function_type
|
function_type
|
||||||
.call(self.db, arg_types.as_slice())
|
.call(self.db, arg_types.as_slice())
|
||||||
.unwrap_with_diagnostic(self.db, func.as_ref().into(), self)
|
.unwrap_with_diagnostic(self.db, func.as_ref().into(), &mut self.diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_starred_expression(&mut self, starred: &ast::ExprStarred) -> Type<'db> {
|
fn infer_starred_expression(&mut self, starred: &ast::ExprStarred) -> Type<'db> {
|
||||||
|
@ -2346,7 +2252,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let iterable_ty = self.infer_expression(value);
|
let iterable_ty = self.infer_expression(value);
|
||||||
iterable_ty
|
iterable_ty
|
||||||
.iterate(self.db)
|
.iterate(self.db)
|
||||||
.unwrap_with_diagnostic(value.as_ref().into(), self);
|
.unwrap_with_diagnostic(value.as_ref().into(), &mut self.diagnostics);
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
Type::Todo
|
Type::Todo
|
||||||
|
@ -2367,7 +2273,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let iterable_ty = self.infer_expression(value);
|
let iterable_ty = self.infer_expression(value);
|
||||||
iterable_ty
|
iterable_ty
|
||||||
.iterate(self.db)
|
.iterate(self.db)
|
||||||
.unwrap_with_diagnostic(value.as_ref().into(), self);
|
.unwrap_with_diagnostic(value.as_ref().into(), &mut self.diagnostics);
|
||||||
|
|
||||||
// TODO get type from `ReturnType` of generator
|
// TODO get type from `ReturnType` of generator
|
||||||
Type::Todo
|
Type::Todo
|
||||||
|
@ -2433,7 +2339,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
if ty.may_be_unbound(self.db) && Some(self.scope()) != builtins_module_scope(self.db) {
|
if ty.may_be_unbound(self.db) && Some(self.scope()) != builtins_module_scope(self.db) {
|
||||||
let mut builtin_ty = builtins_symbol_ty(self.db, name);
|
let mut builtin_ty = builtins_symbol_ty(self.db, name);
|
||||||
if builtin_ty.is_unbound() && name == "reveal_type" {
|
if builtin_ty.is_unbound() && name == "reveal_type" {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
name_node.into(),
|
name_node.into(),
|
||||||
"undefined-reveal",
|
"undefined-reveal",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -2487,13 +2393,13 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
|
|
||||||
let ty = bindings_ty(self.db, definitions, unbound_ty);
|
let ty = bindings_ty(self.db, definitions, unbound_ty);
|
||||||
if ty.is_unbound() {
|
if ty.is_unbound() {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
name.into(),
|
name.into(),
|
||||||
"unresolved-reference",
|
"unresolved-reference",
|
||||||
format_args!("Name `{id}` used when not defined"),
|
format_args!("Name `{id}` used when not defined"),
|
||||||
);
|
);
|
||||||
} else if ty.may_be_unbound(self.db) {
|
} else if ty.may_be_unbound(self.db) {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
name.into(),
|
name.into(),
|
||||||
"possibly-unresolved-reference",
|
"possibly-unresolved-reference",
|
||||||
format_args!("Name `{id}` used when possibly not defined"),
|
format_args!("Name `{id}` used when possibly not defined"),
|
||||||
|
@ -2578,10 +2484,14 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let class_member = class.class_member(self.db, unary_dunder_method);
|
let class_member = class.class_member(self.db, unary_dunder_method);
|
||||||
let call = class_member.call(self.db, &[operand_type]);
|
let call = class_member.call(self.db, &[operand_type]);
|
||||||
|
|
||||||
match call.return_ty_result(self.db, AnyNodeRef::ExprUnaryOp(unary), self) {
|
match call.return_ty_result(
|
||||||
|
self.db,
|
||||||
|
AnyNodeRef::ExprUnaryOp(unary),
|
||||||
|
&mut self.diagnostics,
|
||||||
|
) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
unary.into(),
|
unary.into(),
|
||||||
"unsupported-operator",
|
"unsupported-operator",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -2622,7 +2532,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
|
|
||||||
self.infer_binary_expression_type(left_ty, right_ty, *op)
|
self.infer_binary_expression_type(left_ty, right_ty, *op)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
binary.into(),
|
binary.into(),
|
||||||
"unsupported-operator",
|
"unsupported-operator",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -2926,7 +2836,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_binary_type_comparison(left_ty, *op, right_ty)
|
self.infer_binary_type_comparison(left_ty, *op, right_ty)
|
||||||
.unwrap_or_else(|error| {
|
.unwrap_or_else(|error| {
|
||||||
// Handle unsupported operators (diagnostic, `bool`/`Unknown` outcome)
|
// Handle unsupported operators (diagnostic, `bool`/`Unknown` outcome)
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
AnyNodeRef::ExprCompare(compare),
|
AnyNodeRef::ExprCompare(compare),
|
||||||
"unsupported-operator",
|
"unsupported-operator",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -3295,7 +3205,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
.py_index(i32::try_from(int).expect("checked in branch arm"))
|
.py_index(i32::try_from(int).expect("checked in branch arm"))
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
self.index_out_of_bounds_diagnostic(
|
self.diagnostics.add_index_out_of_bounds(
|
||||||
"tuple",
|
"tuple",
|
||||||
value_node.into(),
|
value_node.into(),
|
||||||
value_ty,
|
value_ty,
|
||||||
|
@ -3314,7 +3224,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let new_elements: Vec<_> = new_elements.copied().collect();
|
let new_elements: Vec<_> = new_elements.copied().collect();
|
||||||
Type::Tuple(TupleType::new(self.db, new_elements.into_boxed_slice()))
|
Type::Tuple(TupleType::new(self.db, new_elements.into_boxed_slice()))
|
||||||
} else {
|
} else {
|
||||||
self.slice_step_size_zero_diagnostic(value_node.into());
|
self.diagnostics.add_slice_step_size_zero(value_node.into());
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3333,7 +3243,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
self.index_out_of_bounds_diagnostic(
|
self.diagnostics.add_index_out_of_bounds(
|
||||||
"string",
|
"string",
|
||||||
value_node.into(),
|
value_node.into(),
|
||||||
value_ty,
|
value_ty,
|
||||||
|
@ -3353,7 +3263,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let literal: String = new_chars.collect();
|
let literal: String = new_chars.collect();
|
||||||
Type::StringLiteral(StringLiteralType::new(self.db, literal.into_boxed_str()))
|
Type::StringLiteral(StringLiteralType::new(self.db, literal.into_boxed_str()))
|
||||||
} else {
|
} else {
|
||||||
self.slice_step_size_zero_diagnostic(value_node.into());
|
self.diagnostics.add_slice_step_size_zero(value_node.into());
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
};
|
};
|
||||||
result
|
result
|
||||||
|
@ -3370,7 +3280,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
Type::BytesLiteral(BytesLiteralType::new(self.db, [*byte].as_slice()))
|
Type::BytesLiteral(BytesLiteralType::new(self.db, [*byte].as_slice()))
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
self.index_out_of_bounds_diagnostic(
|
self.diagnostics.add_index_out_of_bounds(
|
||||||
"bytes literal",
|
"bytes literal",
|
||||||
value_node.into(),
|
value_node.into(),
|
||||||
value_ty,
|
value_ty,
|
||||||
|
@ -3389,7 +3299,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let new_bytes: Vec<u8> = new_bytes.copied().collect();
|
let new_bytes: Vec<u8> = new_bytes.copied().collect();
|
||||||
Type::BytesLiteral(BytesLiteralType::new(self.db, new_bytes.into_boxed_slice()))
|
Type::BytesLiteral(BytesLiteralType::new(self.db, new_bytes.into_boxed_slice()))
|
||||||
} else {
|
} else {
|
||||||
self.slice_step_size_zero_diagnostic(value_node.into());
|
self.diagnostics.add_slice_step_size_zero(value_node.into());
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3413,9 +3323,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
if !dunder_getitem_method.is_unbound() {
|
if !dunder_getitem_method.is_unbound() {
|
||||||
return dunder_getitem_method
|
return dunder_getitem_method
|
||||||
.call(self.db, &[slice_ty])
|
.call(self.db, &[slice_ty])
|
||||||
.return_ty_result(self.db, value_node.into(), self)
|
.return_ty_result(self.db, value_node.into(), &mut self.diagnostics)
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
value_node.into(),
|
value_node.into(),
|
||||||
"call-non-callable",
|
"call-non-callable",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -3442,9 +3352,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
if !dunder_class_getitem_method.is_unbound() {
|
if !dunder_class_getitem_method.is_unbound() {
|
||||||
return dunder_class_getitem_method
|
return dunder_class_getitem_method
|
||||||
.call(self.db, &[slice_ty])
|
.call(self.db, &[slice_ty])
|
||||||
.return_ty_result(self.db, value_node.into(), self)
|
.return_ty_result(self.db, value_node.into(), &mut self.diagnostics)
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
self.add_diagnostic(
|
self.diagnostics.add(
|
||||||
value_node.into(),
|
value_node.into(),
|
||||||
"call-non-callable",
|
"call-non-callable",
|
||||||
format_args!(
|
format_args!(
|
||||||
|
@ -3462,13 +3372,17 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
return KnownClass::GenericAlias.to_instance(self.db);
|
return KnownClass::GenericAlias.to_instance(self.db);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.non_subscriptable_diagnostic(
|
self.diagnostics.add_non_subscriptable(
|
||||||
value_node.into(),
|
value_node.into(),
|
||||||
value_ty,
|
value_ty,
|
||||||
"__class_getitem__",
|
"__class_getitem__",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
self.non_subscriptable_diagnostic(value_node.into(), value_ty, "__getitem__");
|
self.diagnostics.add_non_subscriptable(
|
||||||
|
value_node.into(),
|
||||||
|
value_ty,
|
||||||
|
"__getitem__",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
|
@ -3556,35 +3470,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a new diagnostic.
|
|
||||||
///
|
|
||||||
/// The diagnostic does not get added if the rule isn't enabled for this file.
|
|
||||||
pub(super) fn add_diagnostic(
|
|
||||||
&mut self,
|
|
||||||
node: AnyNodeRef,
|
|
||||||
rule: &str,
|
|
||||||
message: std::fmt::Arguments,
|
|
||||||
) {
|
|
||||||
if !self.db.is_file_open(self.file) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Don't emit the diagnostic if:
|
|
||||||
// * The enclosing node contains any syntax errors
|
|
||||||
// * The rule is disabled for this file. We probably want to introduce a new query that
|
|
||||||
// returns a rule selector for a given file that respects the package's settings,
|
|
||||||
// any global pragma comments in the file, and any per-file-ignores.
|
|
||||||
|
|
||||||
self.types.diagnostics.push(TypeCheckDiagnostic {
|
|
||||||
file: self.file,
|
|
||||||
rule: rule.to_string(),
|
|
||||||
message: message.to_string(),
|
|
||||||
range: node.range(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn finish(mut self) -> TypeInference<'db> {
|
pub(super) fn finish(mut self) -> TypeInference<'db> {
|
||||||
self.infer_region();
|
self.infer_region();
|
||||||
|
self.types.diagnostics = self.diagnostics.finish();
|
||||||
self.types.shrink_to_fit();
|
self.types.shrink_to_fit();
|
||||||
self.types
|
self.types
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue