mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Update unused warnings for inline imports
Now that imports can be limited to smaller scopes than the entire module, unused import warnings need to work like unused def warnings. This commit moves unused import warnings discovery and reporting from load to canonicalization where we can track their usage per scope. This also fixes a longstanding bug where unused exposed names from an import were not reported if they were only used in a qualified manner.
This commit is contained in:
parent
08e6b79dca
commit
7b3317dbb6
18 changed files with 334 additions and 122 deletions
|
@ -35,7 +35,7 @@ import Bool exposing [Bool, Eq]
|
||||||
import Result exposing [Result]
|
import Result exposing [Result]
|
||||||
import List
|
import List
|
||||||
import Str
|
import Str
|
||||||
import Num exposing [Nat, U64, F32, U32, U8, I8]
|
import Num exposing [Nat, U64, F32, U32, U8]
|
||||||
import Hash exposing [Hasher, Hash]
|
import Hash exposing [Hasher, Hash]
|
||||||
import Inspect exposing [Inspect, Inspector, InspectFormatter]
|
import Inspect exposing [Inspect, Inspector, InspectFormatter]
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ interface Hash
|
||||||
hashUnordered,
|
hashUnordered,
|
||||||
] imports []
|
] imports []
|
||||||
|
|
||||||
import Bool exposing [Bool, isEq]
|
import Bool exposing [Bool]
|
||||||
import List
|
import List
|
||||||
import Str
|
import Str
|
||||||
import Num exposing [
|
import Num exposing [
|
||||||
|
|
|
@ -75,7 +75,7 @@ interface List
|
||||||
|
|
||||||
import Bool exposing [Bool, Eq]
|
import Bool exposing [Bool, Eq]
|
||||||
import Result exposing [Result]
|
import Result exposing [Result]
|
||||||
import Num exposing [Nat, Num, Int]
|
import Num exposing [Nat, Num]
|
||||||
|
|
||||||
## ## Types
|
## ## Types
|
||||||
##
|
##
|
||||||
|
|
|
@ -28,7 +28,7 @@ interface Set
|
||||||
|
|
||||||
import List
|
import List
|
||||||
import Bool exposing [Bool, Eq]
|
import Bool exposing [Bool, Eq]
|
||||||
import Dict exposing [Dict]
|
import Dict
|
||||||
import Num exposing [Nat]
|
import Num exposing [Nat]
|
||||||
import Hash exposing [Hash, Hasher]
|
import Hash exposing [Hash, Hasher]
|
||||||
import Inspect exposing [Inspect, Inspector, InspectFormatter]
|
import Inspect exposing [Inspect, Inspector, InspectFormatter]
|
||||||
|
|
|
@ -139,7 +139,7 @@ interface Str
|
||||||
]
|
]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
import Bool exposing [Bool, Eq]
|
import Bool exposing [Bool]
|
||||||
import Result exposing [Result]
|
import Result exposing [Result]
|
||||||
import List
|
import List
|
||||||
import Num exposing [Nat, Num, U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec]
|
import Num exposing [Nat, Num, U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec]
|
||||||
|
|
|
@ -10,11 +10,11 @@ interface TotallyNotJson
|
||||||
|
|
||||||
import List
|
import List
|
||||||
import Str
|
import Str
|
||||||
import Result exposing [Result]
|
import Result
|
||||||
import Encode exposing [Encoder, EncoderFormatting, appendWith]
|
import Encode exposing [EncoderFormatting, appendWith]
|
||||||
import Decode exposing [DecoderFormatting, DecodeResult]
|
import Decode exposing [DecoderFormatting, DecodeResult]
|
||||||
import Num exposing [U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Nat, Dec]
|
import Num exposing [U8, U16, U64, F32, F64, Nat, Dec]
|
||||||
import Bool exposing [Bool, Eq]
|
import Bool exposing [Bool]
|
||||||
|
|
||||||
## An opaque type with the `EncoderFormatting` and
|
## An opaque type with the `EncoderFormatting` and
|
||||||
## `DecoderFormatting` abilities.
|
## `DecoderFormatting` abilities.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::procedure::References;
|
use crate::procedure::{QualifiedReference, References};
|
||||||
use crate::scope::{PendingAbilitiesInScope, Scope};
|
use crate::scope::{PendingAbilitiesInScope, Scope};
|
||||||
use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet};
|
use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet};
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
|
@ -17,10 +17,43 @@ use roc_types::types::{
|
||||||
pub struct Annotation {
|
pub struct Annotation {
|
||||||
pub typ: Type,
|
pub typ: Type,
|
||||||
pub introduced_variables: IntroducedVariables,
|
pub introduced_variables: IntroducedVariables,
|
||||||
pub references: VecSet<Symbol>,
|
pub references: AnnotationReferences,
|
||||||
pub aliases: VecMap<Symbol, Alias>,
|
pub aliases: VecMap<Symbol, Alias>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AnnotationReferences {
|
||||||
|
symbols: VecSet<Symbol>,
|
||||||
|
qualified: Vec<QualifiedReference>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnnotationReferences {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
symbols: Default::default(),
|
||||||
|
qualified: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, symbol: Symbol, qualified: QualifiedReference) {
|
||||||
|
if !self.symbols.insert(symbol) {
|
||||||
|
self.qualified.push(qualified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_lookups(&self, references: &mut References) {
|
||||||
|
for (symbol, qualified) in self.symbols.iter().zip(&self.qualified) {
|
||||||
|
references.insert_type_lookup(*symbol, *qualified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AnnotationReferences {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Annotation {
|
impl Annotation {
|
||||||
pub fn add_to(
|
pub fn add_to(
|
||||||
&self,
|
&self,
|
||||||
|
@ -28,9 +61,7 @@ impl Annotation {
|
||||||
references: &mut References,
|
references: &mut References,
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
) {
|
) {
|
||||||
for symbol in self.references.iter() {
|
self.references.insert_lookups(references);
|
||||||
references.insert_type_lookup(*symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
introduced_variables.union(&self.introduced_variables);
|
introduced_variables.union(&self.introduced_variables);
|
||||||
|
|
||||||
|
@ -291,7 +322,7 @@ pub(crate) fn canonicalize_annotation(
|
||||||
annotation_for: AnnotationFor,
|
annotation_for: AnnotationFor,
|
||||||
) -> Annotation {
|
) -> Annotation {
|
||||||
let mut introduced_variables = IntroducedVariables::default();
|
let mut introduced_variables = IntroducedVariables::default();
|
||||||
let mut references = VecSet::default();
|
let mut references = AnnotationReferences::new();
|
||||||
let mut aliases = VecMap::default();
|
let mut aliases = VecMap::default();
|
||||||
|
|
||||||
let (annotation, region) = match annotation {
|
let (annotation, region) = match annotation {
|
||||||
|
@ -381,13 +412,17 @@ pub(crate) fn make_apply_symbol(
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
ident: &str,
|
ident: &str,
|
||||||
|
references: &mut AnnotationReferences,
|
||||||
) -> Result<Symbol, Type> {
|
) -> Result<Symbol, Type> {
|
||||||
if module_name.is_empty() {
|
if module_name.is_empty() {
|
||||||
// Since module_name was empty, this is an unqualified type.
|
// Since module_name was empty, this is an unqualified type.
|
||||||
// Look it up in scope!
|
// Look it up in scope!
|
||||||
|
|
||||||
match scope.lookup_str(ident, region) {
|
match scope.lookup_str(ident, region) {
|
||||||
Ok(symbol) => Ok(symbol),
|
Ok(symbol) => {
|
||||||
|
references.insert(symbol, QualifiedReference::Unqualified);
|
||||||
|
Ok(symbol)
|
||||||
|
}
|
||||||
Err(problem) => {
|
Err(problem) => {
|
||||||
env.problem(roc_problem::can::Problem::RuntimeError(problem));
|
env.problem(roc_problem::can::Problem::RuntimeError(problem));
|
||||||
|
|
||||||
|
@ -396,7 +431,10 @@ pub(crate) fn make_apply_symbol(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match env.qualified_lookup(scope, module_name, ident, region) {
|
match env.qualified_lookup(scope, module_name, ident, region) {
|
||||||
Ok(symbol) => Ok(symbol),
|
Ok(symbol) => {
|
||||||
|
references.insert(symbol, QualifiedReference::Qualified);
|
||||||
|
Ok(symbol)
|
||||||
|
}
|
||||||
Err(problem) => {
|
Err(problem) => {
|
||||||
// Either the module wasn't imported, or
|
// Either the module wasn't imported, or
|
||||||
// it was imported but it doesn't expose this ident.
|
// it was imported but it doesn't expose this ident.
|
||||||
|
@ -537,7 +575,7 @@ fn can_annotation_help(
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
local_aliases: &mut VecMap<Symbol, Alias>,
|
local_aliases: &mut VecMap<Symbol, Alias>,
|
||||||
references: &mut VecSet<Symbol>,
|
references: &mut AnnotationReferences,
|
||||||
) -> Type {
|
) -> Type {
|
||||||
use roc_parse::ast::TypeAnnotation::*;
|
use roc_parse::ast::TypeAnnotation::*;
|
||||||
|
|
||||||
|
@ -580,15 +618,14 @@ fn can_annotation_help(
|
||||||
Type::Function(args, Box::new(closure), Box::new(ret))
|
Type::Function(args, Box::new(closure), Box::new(ret))
|
||||||
}
|
}
|
||||||
Apply(module_name, ident, type_arguments) => {
|
Apply(module_name, ident, type_arguments) => {
|
||||||
let symbol = match make_apply_symbol(env, region, scope, module_name, ident) {
|
let symbol = match make_apply_symbol(env, region, scope, module_name, ident, references)
|
||||||
|
{
|
||||||
Err(problem) => return problem,
|
Err(problem) => return problem,
|
||||||
Ok(symbol) => symbol,
|
Ok(symbol) => symbol,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
|
|
||||||
references.insert(symbol);
|
|
||||||
|
|
||||||
if scope.abilities_store.is_ability(symbol) {
|
if scope.abilities_store.is_ability(symbol) {
|
||||||
let fresh_ty_var = find_fresh_var_name(introduced_variables);
|
let fresh_ty_var = find_fresh_var_name(introduced_variables);
|
||||||
|
|
||||||
|
@ -744,7 +781,7 @@ fn can_annotation_help(
|
||||||
let mut vars = Vec::with_capacity(loc_vars.len());
|
let mut vars = Vec::with_capacity(loc_vars.len());
|
||||||
let mut lowercase_vars: Vec<Loc<AliasVar>> = Vec::with_capacity(loc_vars.len());
|
let mut lowercase_vars: Vec<Loc<AliasVar>> = Vec::with_capacity(loc_vars.len());
|
||||||
|
|
||||||
references.insert(symbol);
|
references.insert(symbol, QualifiedReference::Unqualified);
|
||||||
|
|
||||||
for loc_var in *loc_vars {
|
for loc_var in *loc_vars {
|
||||||
let var = match loc_var.value {
|
let var = match loc_var.value {
|
||||||
|
@ -1055,7 +1092,7 @@ fn canonicalize_has_clause(
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
clause: &Loc<roc_parse::ast::ImplementsClause<'_>>,
|
clause: &Loc<roc_parse::ast::ImplementsClause<'_>>,
|
||||||
pending_abilities_in_scope: &PendingAbilitiesInScope,
|
pending_abilities_in_scope: &PendingAbilitiesInScope,
|
||||||
references: &mut VecSet<Symbol>,
|
references: &mut AnnotationReferences,
|
||||||
) -> Result<(), Type> {
|
) -> Result<(), Type> {
|
||||||
let Loc {
|
let Loc {
|
||||||
region,
|
region,
|
||||||
|
@ -1078,7 +1115,7 @@ fn canonicalize_has_clause(
|
||||||
{
|
{
|
||||||
let ability = match ability {
|
let ability = match ability {
|
||||||
TypeAnnotation::Apply(module_name, ident, _type_arguments) => {
|
TypeAnnotation::Apply(module_name, ident, _type_arguments) => {
|
||||||
let symbol = make_apply_symbol(env, region, scope, module_name, ident)?;
|
let symbol = make_apply_symbol(env, region, scope, module_name, ident, references)?;
|
||||||
|
|
||||||
// Ability defined locally, whose members we are constructing right now...
|
// Ability defined locally, whose members we are constructing right now...
|
||||||
if !pending_abilities_in_scope.contains_key(&symbol)
|
if !pending_abilities_in_scope.contains_key(&symbol)
|
||||||
|
@ -1096,7 +1133,6 @@ fn canonicalize_has_clause(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
references.insert(ability);
|
|
||||||
let already_seen = can_abilities.insert(ability);
|
let already_seen = can_abilities.insert(ability);
|
||||||
|
|
||||||
if already_seen {
|
if already_seen {
|
||||||
|
@ -1130,7 +1166,7 @@ fn can_extension_type(
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
local_aliases: &mut VecMap<Symbol, Alias>,
|
local_aliases: &mut VecMap<Symbol, Alias>,
|
||||||
references: &mut VecSet<Symbol>,
|
references: &mut AnnotationReferences,
|
||||||
opt_ext: &Option<&Loc<TypeAnnotation>>,
|
opt_ext: &Option<&Loc<TypeAnnotation>>,
|
||||||
ext_problem_kind: roc_problem::can::ExtensionTypeKind,
|
ext_problem_kind: roc_problem::can::ExtensionTypeKind,
|
||||||
) -> (Type, ExtImplicitOpenness) {
|
) -> (Type, ExtImplicitOpenness) {
|
||||||
|
@ -1333,7 +1369,7 @@ fn can_assigned_fields<'a>(
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
local_aliases: &mut VecMap<Symbol, Alias>,
|
local_aliases: &mut VecMap<Symbol, Alias>,
|
||||||
references: &mut VecSet<Symbol>,
|
references: &mut AnnotationReferences,
|
||||||
) -> SendMap<Lowercase, RecordField<Type>> {
|
) -> SendMap<Lowercase, RecordField<Type>> {
|
||||||
use roc_parse::ast::AssignedField::*;
|
use roc_parse::ast::AssignedField::*;
|
||||||
use roc_types::types::RecordField::*;
|
use roc_types::types::RecordField::*;
|
||||||
|
@ -1448,7 +1484,7 @@ fn can_assigned_tuple_elems(
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
local_aliases: &mut VecMap<Symbol, Alias>,
|
local_aliases: &mut VecMap<Symbol, Alias>,
|
||||||
references: &mut VecSet<Symbol>,
|
references: &mut AnnotationReferences,
|
||||||
) -> VecMap<usize, Type> {
|
) -> VecMap<usize, Type> {
|
||||||
let mut elem_types = VecMap::with_capacity(elems.len());
|
let mut elem_types = VecMap::with_capacity(elems.len());
|
||||||
|
|
||||||
|
@ -1482,7 +1518,7 @@ fn can_tags<'a>(
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
local_aliases: &mut VecMap<Symbol, Alias>,
|
local_aliases: &mut VecMap<Symbol, Alias>,
|
||||||
references: &mut VecSet<Symbol>,
|
references: &mut AnnotationReferences,
|
||||||
) -> Vec<(TagName, Vec<Type>)> {
|
) -> Vec<(TagName, Vec<Type>)> {
|
||||||
let mut tag_types = Vec::with_capacity(tags.len());
|
let mut tag_types = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::annotation::canonicalize_annotation;
|
||||||
use crate::annotation::find_type_def_symbols;
|
use crate::annotation::find_type_def_symbols;
|
||||||
use crate::annotation::make_apply_symbol;
|
use crate::annotation::make_apply_symbol;
|
||||||
use crate::annotation::AnnotationFor;
|
use crate::annotation::AnnotationFor;
|
||||||
|
use crate::annotation::AnnotationReferences;
|
||||||
use crate::annotation::IntroducedVariables;
|
use crate::annotation::IntroducedVariables;
|
||||||
use crate::annotation::OwnedNamedOrAble;
|
use crate::annotation::OwnedNamedOrAble;
|
||||||
use crate::derive;
|
use crate::derive;
|
||||||
|
@ -358,9 +359,7 @@ fn canonicalize_alias<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Record all the annotation's references in output.references.lookups
|
// Record all the annotation's references in output.references.lookups
|
||||||
for symbol in can_ann.references {
|
can_ann.references.insert_lookups(&mut output.references);
|
||||||
output.references.insert_type_lookup(symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut can_vars: Vec<Loc<AliasVar>> = Vec::with_capacity(vars.len());
|
let mut can_vars: Vec<Loc<AliasVar>> = Vec::with_capacity(vars.len());
|
||||||
let mut is_phantom = false;
|
let mut is_phantom = false;
|
||||||
|
@ -704,6 +703,8 @@ fn canonicalize_opaque<'a>(
|
||||||
AliasKind::Opaque,
|
AliasKind::Opaque,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let mut references = AnnotationReferences::new();
|
||||||
|
|
||||||
let mut derived_defs = Vec::new();
|
let mut derived_defs = Vec::new();
|
||||||
if let Some(has_abilities) = has_abilities {
|
if let Some(has_abilities) = has_abilities {
|
||||||
let has_abilities = has_abilities.value.collection();
|
let has_abilities = has_abilities.value.collection();
|
||||||
|
@ -722,7 +723,8 @@ fn canonicalize_opaque<'a>(
|
||||||
// Op := {} has [Eq]
|
// Op := {} has [Eq]
|
||||||
let (ability, members) = match ability.value {
|
let (ability, members) = match ability.value {
|
||||||
ast::TypeAnnotation::Apply(module_name, ident, []) => {
|
ast::TypeAnnotation::Apply(module_name, ident, []) => {
|
||||||
match make_apply_symbol(env, region, scope, module_name, ident) {
|
match make_apply_symbol(env, region, scope, module_name, ident, &mut references)
|
||||||
|
{
|
||||||
Ok(ability) => {
|
Ok(ability) => {
|
||||||
let opt_members = scope
|
let opt_members = scope
|
||||||
.abilities_store
|
.abilities_store
|
||||||
|
@ -915,6 +917,8 @@ fn canonicalize_opaque<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
references.insert_lookups(&mut output.references);
|
||||||
|
|
||||||
Ok(CanonicalizedOpaque {
|
Ok(CanonicalizedOpaque {
|
||||||
opaque_def: alias,
|
opaque_def: alias,
|
||||||
derived_defs,
|
derived_defs,
|
||||||
|
@ -929,7 +933,12 @@ pub(crate) fn canonicalize_defs<'a>(
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
loc_defs: &'a mut roc_parse::ast::Defs<'a>,
|
loc_defs: &'a mut roc_parse::ast::Defs<'a>,
|
||||||
pattern_type: PatternType,
|
pattern_type: PatternType,
|
||||||
) -> (CanDefs, Output, MutMap<Symbol, Region>) {
|
) -> (
|
||||||
|
CanDefs,
|
||||||
|
Output,
|
||||||
|
MutMap<Symbol, Region>,
|
||||||
|
Vec<IntroducedImport>,
|
||||||
|
) {
|
||||||
// Canonicalizing defs while detecting shadowing involves a multi-step process:
|
// Canonicalizing defs while detecting shadowing involves a multi-step process:
|
||||||
//
|
//
|
||||||
// 1. Go through each of the patterns.
|
// 1. Go through each of the patterns.
|
||||||
|
@ -979,6 +988,7 @@ pub(crate) fn canonicalize_defs<'a>(
|
||||||
env,
|
env,
|
||||||
var_store,
|
var_store,
|
||||||
value_def,
|
value_def,
|
||||||
|
region,
|
||||||
scope,
|
scope,
|
||||||
&pending_abilities_in_scope,
|
&pending_abilities_in_scope,
|
||||||
&mut output,
|
&mut output,
|
||||||
|
@ -1035,7 +1045,12 @@ fn canonicalize_value_defs<'a>(
|
||||||
pattern_type: PatternType,
|
pattern_type: PatternType,
|
||||||
mut aliases: VecMap<Symbol, Alias>,
|
mut aliases: VecMap<Symbol, Alias>,
|
||||||
mut symbols_introduced: MutMap<Symbol, Region>,
|
mut symbols_introduced: MutMap<Symbol, Region>,
|
||||||
) -> (CanDefs, Output, MutMap<Symbol, Region>) {
|
) -> (
|
||||||
|
CanDefs,
|
||||||
|
Output,
|
||||||
|
MutMap<Symbol, Region>,
|
||||||
|
Vec<IntroducedImport>,
|
||||||
|
) {
|
||||||
// Canonicalize all the patterns, record shadowing problems, and store
|
// Canonicalize all the patterns, record shadowing problems, and store
|
||||||
// the ast::Expr values in pending_exprs for further canonicalization
|
// the ast::Expr values in pending_exprs for further canonicalization
|
||||||
// once we've finished assembling the entire scope.
|
// once we've finished assembling the entire scope.
|
||||||
|
@ -1045,6 +1060,8 @@ fn canonicalize_value_defs<'a>(
|
||||||
let mut pending_expect_fx = Vec::with_capacity(value_defs.len());
|
let mut pending_expect_fx = Vec::with_capacity(value_defs.len());
|
||||||
let mut pending_ingested_files = Vec::with_capacity(value_defs.len());
|
let mut pending_ingested_files = Vec::with_capacity(value_defs.len());
|
||||||
|
|
||||||
|
let mut imports_introduced = Vec::with_capacity(value_defs.len());
|
||||||
|
|
||||||
for loc_pending_def in value_defs {
|
for loc_pending_def in value_defs {
|
||||||
match loc_pending_def.value {
|
match loc_pending_def.value {
|
||||||
PendingValue::Def(pending_def) => {
|
PendingValue::Def(pending_def) => {
|
||||||
|
@ -1064,7 +1081,9 @@ fn canonicalize_value_defs<'a>(
|
||||||
PendingValue::ExpectFx(pending_expect) => {
|
PendingValue::ExpectFx(pending_expect) => {
|
||||||
pending_expect_fx.push(pending_expect);
|
pending_expect_fx.push(pending_expect);
|
||||||
}
|
}
|
||||||
PendingValue::ModuleImport => { /* nothing to do */ }
|
PendingValue::ModuleImport(introduced_import) => {
|
||||||
|
imports_introduced.push(introduced_import);
|
||||||
|
}
|
||||||
PendingValue::IngestedFileImport(pending_ingested_file) => {
|
PendingValue::IngestedFileImport(pending_ingested_file) => {
|
||||||
pending_ingested_files.push(pending_ingested_file);
|
pending_ingested_files.push(pending_ingested_file);
|
||||||
}
|
}
|
||||||
|
@ -1181,7 +1200,7 @@ fn canonicalize_value_defs<'a>(
|
||||||
aliases,
|
aliases,
|
||||||
};
|
};
|
||||||
|
|
||||||
(can_defs, output, symbols_introduced)
|
(can_defs, output, symbols_introduced, imports_introduced)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CanonicalizedTypeDefs<'a> {
|
struct CanonicalizedTypeDefs<'a> {
|
||||||
|
@ -1395,9 +1414,9 @@ fn resolve_abilities(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Record all the annotation's references in output.references.lookups
|
// Record all the annotation's references in output.references.lookups
|
||||||
for symbol in member_annot.references {
|
member_annot
|
||||||
output.references.insert_type_lookup(symbol);
|
.references
|
||||||
}
|
.insert_lookups(&mut output.references);
|
||||||
|
|
||||||
// What variables in the annotation are bound to the parent ability, and what variables
|
// What variables in the annotation are bound to the parent ability, and what variables
|
||||||
// are bound to some other ability?
|
// are bound to some other ability?
|
||||||
|
@ -2470,7 +2489,7 @@ pub fn can_defs_with_return<'a>(
|
||||||
loc_defs: &'a mut Defs<'a>,
|
loc_defs: &'a mut Defs<'a>,
|
||||||
loc_ret: &'a Loc<ast::Expr<'a>>,
|
loc_ret: &'a Loc<ast::Expr<'a>>,
|
||||||
) -> (Expr, Output) {
|
) -> (Expr, Output) {
|
||||||
let (unsorted, defs_output, symbols_introduced) = canonicalize_defs(
|
let (unsorted, defs_output, symbols_introduced, imports_introduced) = canonicalize_defs(
|
||||||
env,
|
env,
|
||||||
Output::default(),
|
Output::default(),
|
||||||
var_store,
|
var_store,
|
||||||
|
@ -2504,6 +2523,8 @@ pub fn can_defs_with_return<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
report_unused_imports(imports_introduced, &output.references, env, scope);
|
||||||
|
|
||||||
let mut loc_expr: Loc<Expr> = ret_expr;
|
let mut loc_expr: Loc<Expr> = ret_expr;
|
||||||
|
|
||||||
for declaration in declarations.into_iter().rev() {
|
for declaration in declarations.into_iter().rev() {
|
||||||
|
@ -2513,6 +2534,27 @@ pub fn can_defs_with_return<'a>(
|
||||||
(loc_expr.value, output)
|
(loc_expr.value, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn report_unused_imports(
|
||||||
|
imports_introduced: Vec<IntroducedImport>,
|
||||||
|
references: &References,
|
||||||
|
env: &mut Env<'_>,
|
||||||
|
scope: &mut Scope,
|
||||||
|
) {
|
||||||
|
for import in imports_introduced {
|
||||||
|
if references.has_module_lookup(import.module_id) {
|
||||||
|
for (symbol, region) in import.exposed_symbols {
|
||||||
|
if !references.has_unqualified_type_or_value_lookup(symbol)
|
||||||
|
&& !scope.abilities_store.is_specialization_name(symbol)
|
||||||
|
{
|
||||||
|
env.problem(Problem::UnusedImport(symbol, region));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
env.problem(Problem::UnusedModuleImport(import.module_id, import.region));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
|
fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
|
||||||
match decl {
|
match decl {
|
||||||
Declaration::Declare(def) => {
|
Declaration::Declare(def) => {
|
||||||
|
@ -2760,7 +2802,7 @@ enum PendingValue<'a> {
|
||||||
Dbg(PendingExpectOrDbg<'a>),
|
Dbg(PendingExpectOrDbg<'a>),
|
||||||
Expect(PendingExpectOrDbg<'a>),
|
Expect(PendingExpectOrDbg<'a>),
|
||||||
ExpectFx(PendingExpectOrDbg<'a>),
|
ExpectFx(PendingExpectOrDbg<'a>),
|
||||||
ModuleImport,
|
ModuleImport(IntroducedImport),
|
||||||
IngestedFileImport(ast::IngestedFileImport<'a>),
|
IngestedFileImport(ast::IngestedFileImport<'a>),
|
||||||
SignatureDefMismatch,
|
SignatureDefMismatch,
|
||||||
}
|
}
|
||||||
|
@ -2770,10 +2812,18 @@ struct PendingExpectOrDbg<'a> {
|
||||||
preceding_comment: Region,
|
preceding_comment: Region,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct IntroducedImport {
|
||||||
|
module_id: ModuleId,
|
||||||
|
region: Region,
|
||||||
|
exposed_symbols: Vec<(Symbol, Region)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn to_pending_value_def<'a>(
|
fn to_pending_value_def<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
def: &'a ast::ValueDef<'a>,
|
def: &'a ast::ValueDef<'a>,
|
||||||
|
region: Region,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
pending_abilities_in_scope: &PendingAbilitiesInScope,
|
pending_abilities_in_scope: &PendingAbilitiesInScope,
|
||||||
output: &mut Output,
|
output: &mut Output,
|
||||||
|
@ -2896,14 +2946,20 @@ fn to_pending_value_def<'a>(
|
||||||
|
|
||||||
scope.import_module(module_id);
|
scope.import_module(module_id);
|
||||||
|
|
||||||
|
let mut exposed_symbols;
|
||||||
|
|
||||||
match module_import.exposed {
|
match module_import.exposed {
|
||||||
None => {}
|
None => {
|
||||||
|
exposed_symbols = Vec::new();
|
||||||
|
}
|
||||||
Some(exposed_kw) => {
|
Some(exposed_kw) => {
|
||||||
let exposed_ids = env
|
let exposed_ids = env
|
||||||
.dep_idents
|
.dep_idents
|
||||||
.get(&module_id)
|
.get(&module_id)
|
||||||
.expect("Module id should have been added in load");
|
.expect("Module id should have been added in load");
|
||||||
|
|
||||||
|
exposed_symbols = Vec::with_capacity(exposed_kw.item.len());
|
||||||
|
|
||||||
for loc_name in exposed_kw.item.items {
|
for loc_name in exposed_kw.item.items {
|
||||||
let exposed_name = loc_name.value.item();
|
let exposed_name = loc_name.value.item();
|
||||||
let name = exposed_name.as_str();
|
let name = exposed_name.as_str();
|
||||||
|
@ -2912,6 +2968,7 @@ fn to_pending_value_def<'a>(
|
||||||
match exposed_ids.get_id(name) {
|
match exposed_ids.get_id(name) {
|
||||||
Some(ident_id) => {
|
Some(ident_id) => {
|
||||||
let symbol = Symbol::new(module_id, ident_id);
|
let symbol = Symbol::new(module_id, ident_id);
|
||||||
|
exposed_symbols.push((symbol, loc_name.region));
|
||||||
|
|
||||||
match scope.import_symbol(ident, symbol, loc_name.region) {
|
match scope.import_symbol(ident, symbol, loc_name.region) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
|
@ -2943,7 +3000,11 @@ fn to_pending_value_def<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PendingValue::ModuleImport
|
PendingValue::ModuleImport(IntroducedImport {
|
||||||
|
module_id,
|
||||||
|
region,
|
||||||
|
exposed_symbols,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
IngestedFileImport(module_import) => PendingValue::IngestedFileImport(*module_import),
|
IngestedFileImport(module_import) => PendingValue::IngestedFileImport(*module_import),
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::num::{
|
||||||
int_expr_from_result, num_expr_from_result, FloatBound, IntBound, NumBound,
|
int_expr_from_result, num_expr_from_result, FloatBound, IntBound, NumBound,
|
||||||
};
|
};
|
||||||
use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows};
|
use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows};
|
||||||
use crate::procedure::References;
|
use crate::procedure::{QualifiedReference, References};
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use crate::traverse::{walk_expr, Visitor};
|
use crate::traverse::{walk_expr, Visitor};
|
||||||
use roc_collections::soa::Index;
|
use roc_collections::soa::Index;
|
||||||
|
@ -882,7 +882,9 @@ pub fn canonicalize_expr<'a>(
|
||||||
}
|
}
|
||||||
Ok((name, opaque_def)) => {
|
Ok((name, opaque_def)) => {
|
||||||
let argument = Box::new(args.pop().unwrap());
|
let argument = Box::new(args.pop().unwrap());
|
||||||
output.references.insert_type_lookup(name);
|
output
|
||||||
|
.references
|
||||||
|
.insert_type_lookup(name, QualifiedReference::Unqualified);
|
||||||
|
|
||||||
let (type_arguments, lambda_set_variables, specialized_def_type) =
|
let (type_arguments, lambda_set_variables, specialized_def_type) =
|
||||||
freshen_opaque_def(var_store, opaque_def);
|
freshen_opaque_def(var_store, opaque_def);
|
||||||
|
@ -1193,7 +1195,9 @@ pub fn canonicalize_expr<'a>(
|
||||||
}
|
}
|
||||||
Ok((name, opaque_def)) => {
|
Ok((name, opaque_def)) => {
|
||||||
let mut output = Output::default();
|
let mut output = Output::default();
|
||||||
output.references.insert_type_lookup(name);
|
output
|
||||||
|
.references
|
||||||
|
.insert_type_lookup(name, QualifiedReference::Unqualified);
|
||||||
|
|
||||||
let (type_arguments, lambda_set_variables, specialized_def_type) =
|
let (type_arguments, lambda_set_variables, specialized_def_type) =
|
||||||
freshen_opaque_def(var_store, opaque_def);
|
freshen_opaque_def(var_store, opaque_def);
|
||||||
|
@ -1877,7 +1881,9 @@ fn canonicalize_var_lookup(
|
||||||
// Look it up in scope!
|
// Look it up in scope!
|
||||||
match scope.lookup_str(ident, region) {
|
match scope.lookup_str(ident, region) {
|
||||||
Ok(symbol) => {
|
Ok(symbol) => {
|
||||||
output.references.insert_value_lookup(symbol);
|
output
|
||||||
|
.references
|
||||||
|
.insert_value_lookup(symbol, QualifiedReference::Unqualified);
|
||||||
|
|
||||||
if scope.abilities_store.is_ability_member_name(symbol) {
|
if scope.abilities_store.is_ability_member_name(symbol) {
|
||||||
AbilityMember(
|
AbilityMember(
|
||||||
|
@ -1900,7 +1906,9 @@ fn canonicalize_var_lookup(
|
||||||
// Look it up in the env!
|
// Look it up in the env!
|
||||||
match env.qualified_lookup(scope, module_name, ident, region) {
|
match env.qualified_lookup(scope, module_name, ident, region) {
|
||||||
Ok(symbol) => {
|
Ok(symbol) => {
|
||||||
output.references.insert_value_lookup(symbol);
|
output
|
||||||
|
.references
|
||||||
|
.insert_value_lookup(symbol, QualifiedReference::Qualified);
|
||||||
|
|
||||||
if scope.abilities_store.is_ability_member_name(symbol) {
|
if scope.abilities_store.is_ability_member_name(symbol) {
|
||||||
AbilityMember(
|
AbilityMember(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
|
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
|
||||||
use crate::annotation::{canonicalize_annotation, AnnotationFor};
|
use crate::annotation::{canonicalize_annotation, AnnotationFor, AnnotationReferences};
|
||||||
use crate::def::{canonicalize_defs, Def};
|
use crate::def::{canonicalize_defs, report_unused_imports, Def};
|
||||||
use crate::effect_module::HostedGeneratedFunctions;
|
use crate::effect_module::HostedGeneratedFunctions;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::expr::{
|
use crate::expr::{
|
||||||
|
@ -127,7 +127,6 @@ pub struct Module {
|
||||||
pub exposed_imports: MutMap<Symbol, Region>,
|
pub exposed_imports: MutMap<Symbol, Region>,
|
||||||
pub exposed_symbols: VecSet<Symbol>,
|
pub exposed_symbols: VecSet<Symbol>,
|
||||||
pub referenced_values: VecSet<Symbol>,
|
pub referenced_values: VecSet<Symbol>,
|
||||||
pub referenced_types: VecSet<Symbol>,
|
|
||||||
/// all aliases. `bool` indicates whether it is exposed
|
/// all aliases. `bool` indicates whether it is exposed
|
||||||
pub aliases: MutMap<Symbol, (bool, Alias)>,
|
pub aliases: MutMap<Symbol, (bool, Alias)>,
|
||||||
pub rigid_variables: RigidVariables,
|
pub rigid_variables: RigidVariables,
|
||||||
|
@ -152,7 +151,6 @@ pub struct ModuleOutput {
|
||||||
pub exposed_symbols: VecSet<Symbol>,
|
pub exposed_symbols: VecSet<Symbol>,
|
||||||
pub problems: Vec<Problem>,
|
pub problems: Vec<Problem>,
|
||||||
pub referenced_values: VecSet<Symbol>,
|
pub referenced_values: VecSet<Symbol>,
|
||||||
pub referenced_types: VecSet<Symbol>,
|
|
||||||
pub symbols_from_requires: Vec<(Loc<Symbol>, Loc<Type>)>,
|
pub symbols_from_requires: Vec<(Loc<Symbol>, Loc<Type>)>,
|
||||||
pub pending_derives: PendingDerives,
|
pub pending_derives: PendingDerives,
|
||||||
pub scope: Scope,
|
pub scope: Scope,
|
||||||
|
@ -363,7 +361,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (defs, output, symbols_introduced) = canonicalize_defs(
|
let (defs, output, symbols_introduced, imports_introduced) = canonicalize_defs(
|
||||||
&mut env,
|
&mut env,
|
||||||
Output::default(),
|
Output::default(),
|
||||||
var_store,
|
var_store,
|
||||||
|
@ -389,6 +387,8 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
report_unused_imports(imports_introduced, &output.references, &mut env, &mut scope);
|
||||||
|
|
||||||
for named in output.introduced_variables.named {
|
for named in output.introduced_variables.named {
|
||||||
rigid_variables.named.insert(named.variable, named.name);
|
rigid_variables.named.insert(named.variable, named.name);
|
||||||
}
|
}
|
||||||
|
@ -404,18 +404,15 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut referenced_values = VecSet::default();
|
let mut referenced_values = VecSet::default();
|
||||||
let mut referenced_types = VecSet::default();
|
|
||||||
|
|
||||||
// Gather up all the symbols that were referenced across all the defs' lookups.
|
// Gather up all the symbols that were referenced across all the defs' lookups.
|
||||||
referenced_values.extend(output.references.value_lookups().copied());
|
referenced_values.extend(output.references.value_lookups().copied());
|
||||||
referenced_types.extend(output.references.type_lookups().copied());
|
|
||||||
|
|
||||||
// Gather up all the symbols that were referenced across all the defs' calls.
|
// Gather up all the symbols that were referenced across all the defs' calls.
|
||||||
referenced_values.extend(output.references.calls().copied());
|
referenced_values.extend(output.references.calls().copied());
|
||||||
|
|
||||||
// Gather up all the symbols that were referenced from other modules.
|
// Gather up all the symbols that were referenced from other modules.
|
||||||
referenced_values.extend(env.qualified_value_lookups.iter().copied());
|
referenced_values.extend(env.qualified_value_lookups.iter().copied());
|
||||||
referenced_types.extend(env.qualified_type_lookups.iter().copied());
|
|
||||||
|
|
||||||
// NOTE previously we inserted builtin defs into the list of defs here
|
// NOTE previously we inserted builtin defs into the list of defs here
|
||||||
// this is now done later, in file.rs.
|
// this is now done later, in file.rs.
|
||||||
|
@ -539,7 +536,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
let annotation = crate::annotation::Annotation {
|
let annotation = crate::annotation::Annotation {
|
||||||
typ: def_annotation.signature,
|
typ: def_annotation.signature,
|
||||||
introduced_variables: def_annotation.introduced_variables,
|
introduced_variables: def_annotation.introduced_variables,
|
||||||
references: Default::default(),
|
references: AnnotationReferences::new(),
|
||||||
aliases: Default::default(),
|
aliases: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -597,7 +594,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
let annotation = crate::annotation::Annotation {
|
let annotation = crate::annotation::Annotation {
|
||||||
typ: def_annotation.signature,
|
typ: def_annotation.signature,
|
||||||
introduced_variables: def_annotation.introduced_variables,
|
introduced_variables: def_annotation.introduced_variables,
|
||||||
references: Default::default(),
|
references: AnnotationReferences::new(),
|
||||||
aliases: Default::default(),
|
aliases: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -694,14 +691,12 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
|
|
||||||
// Incorporate any remaining output.lookups entries into references.
|
// Incorporate any remaining output.lookups entries into references.
|
||||||
referenced_values.extend(output.references.value_lookups().copied());
|
referenced_values.extend(output.references.value_lookups().copied());
|
||||||
referenced_types.extend(output.references.type_lookups().copied());
|
|
||||||
|
|
||||||
// Incorporate any remaining output.calls entries into references.
|
// Incorporate any remaining output.calls entries into references.
|
||||||
referenced_values.extend(output.references.calls().copied());
|
referenced_values.extend(output.references.calls().copied());
|
||||||
|
|
||||||
// Gather up all the symbols that were referenced from other modules.
|
// Gather up all the symbols that were referenced from other modules.
|
||||||
referenced_values.extend(env.qualified_value_lookups.iter().copied());
|
referenced_values.extend(env.qualified_value_lookups.iter().copied());
|
||||||
referenced_types.extend(env.qualified_type_lookups.iter().copied());
|
|
||||||
|
|
||||||
let mut fix_closures_no_capture_symbols = VecSet::default();
|
let mut fix_closures_no_capture_symbols = VecSet::default();
|
||||||
let mut fix_closures_closure_captures = VecMap::default();
|
let mut fix_closures_closure_captures = VecMap::default();
|
||||||
|
@ -797,7 +792,6 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
rigid_variables,
|
rigid_variables,
|
||||||
declarations,
|
declarations,
|
||||||
referenced_values,
|
referenced_values,
|
||||||
referenced_types,
|
|
||||||
exposed_imports: can_exposed_imports,
|
exposed_imports: can_exposed_imports,
|
||||||
problems: env.problems,
|
problems: env.problems,
|
||||||
symbols_from_requires,
|
symbols_from_requires,
|
||||||
|
|
|
@ -446,7 +446,10 @@ pub fn canonicalize_pattern<'a>(
|
||||||
let (type_arguments, lambda_set_variables, specialized_def_type) =
|
let (type_arguments, lambda_set_variables, specialized_def_type) =
|
||||||
freshen_opaque_def(var_store, opaque_def);
|
freshen_opaque_def(var_store, opaque_def);
|
||||||
|
|
||||||
output.references.insert_type_lookup(opaque);
|
output.references.insert_type_lookup(
|
||||||
|
opaque,
|
||||||
|
crate::procedure::QualifiedReference::Unqualified,
|
||||||
|
);
|
||||||
|
|
||||||
Pattern::UnwrappedOpaque {
|
Pattern::UnwrappedOpaque {
|
||||||
whole_var: var_store.fresh(),
|
whole_var: var_store.fresh(),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
use crate::pattern::Pattern;
|
use crate::pattern::Pattern;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
|
|
||||||
|
@ -46,6 +46,22 @@ impl ReferencesBitflags {
|
||||||
const TYPE_LOOKUP: Self = ReferencesBitflags(2);
|
const TYPE_LOOKUP: Self = ReferencesBitflags(2);
|
||||||
const CALL: Self = ReferencesBitflags(4);
|
const CALL: Self = ReferencesBitflags(4);
|
||||||
const BOUND: Self = ReferencesBitflags(8);
|
const BOUND: Self = ReferencesBitflags(8);
|
||||||
|
const QUALIFIED: Self = ReferencesBitflags(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum QualifiedReference {
|
||||||
|
Unqualified,
|
||||||
|
Qualified,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QualifiedReference {
|
||||||
|
fn flags(&self, flags: ReferencesBitflags) -> ReferencesBitflags {
|
||||||
|
match self {
|
||||||
|
Self::Unqualified => flags,
|
||||||
|
Self::Qualified => ReferencesBitflags(flags.0 | ReferencesBitflags::QUALIFIED.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
@ -108,12 +124,12 @@ impl References {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_value_lookup(&mut self, symbol: Symbol) {
|
pub fn insert_value_lookup(&mut self, symbol: Symbol, qualified: QualifiedReference) {
|
||||||
self.insert(symbol, ReferencesBitflags::VALUE_LOOKUP);
|
self.insert(symbol, qualified.flags(ReferencesBitflags::VALUE_LOOKUP));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_type_lookup(&mut self, symbol: Symbol) {
|
pub fn insert_type_lookup(&mut self, symbol: Symbol, qualified: QualifiedReference) {
|
||||||
self.insert(symbol, ReferencesBitflags::TYPE_LOOKUP);
|
self.insert(symbol, qualified.flags(ReferencesBitflags::TYPE_LOOKUP));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_bound(&mut self, symbol: Symbol) {
|
pub fn insert_bound(&mut self, symbol: Symbol) {
|
||||||
|
@ -178,7 +194,24 @@ impl References {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_unqualified_type_or_value_lookup(&self, symbol: Symbol) -> bool {
|
||||||
|
let mask = ReferencesBitflags::VALUE_LOOKUP.0 | ReferencesBitflags::TYPE_LOOKUP.0;
|
||||||
|
let it = self.symbols.iter().zip(self.bitflags.iter());
|
||||||
|
|
||||||
|
for (a, b) in it {
|
||||||
|
if *a == symbol && b.0 & mask > 0 && b.0 & ReferencesBitflags::QUALIFIED.0 == 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn references_type_def(&self, symbol: Symbol) -> bool {
|
pub fn references_type_def(&self, symbol: Symbol) -> bool {
|
||||||
self.has_type_lookup(symbol)
|
self.has_type_lookup(symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_module_lookup(&self, module_id: ModuleId) -> bool {
|
||||||
|
self.symbols.iter().any(|sym| sym.module_id() == module_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2142,44 +2142,6 @@ macro_rules! debug_check_ir {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report modules that are imported, but from which nothing is used
|
|
||||||
fn report_unused_imported_modules(
|
|
||||||
state: &mut State<'_>,
|
|
||||||
module_id: ModuleId,
|
|
||||||
constrained_module: &ConstrainedModule,
|
|
||||||
) {
|
|
||||||
// [modules-revamp] TODO: take expr-level into account
|
|
||||||
let mut unused_imported_modules = constrained_module.imported_modules.clone();
|
|
||||||
let mut unused_imports = constrained_module.module.exposed_imports.clone();
|
|
||||||
|
|
||||||
for symbol in constrained_module.module.referenced_values.iter() {
|
|
||||||
unused_imported_modules.remove(&symbol.module_id());
|
|
||||||
unused_imports.remove(symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
for symbol in constrained_module.module.referenced_types.iter() {
|
|
||||||
unused_imported_modules.remove(&symbol.module_id());
|
|
||||||
unused_imports.remove(symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
let existing = match state.module_cache.can_problems.entry(module_id) {
|
|
||||||
Vacant(entry) => entry.insert(std::vec::Vec::new()),
|
|
||||||
Occupied(entry) => entry.into_mut(),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (unused, region) in unused_imported_modules.drain() {
|
|
||||||
if !unused.is_builtin() {
|
|
||||||
existing.push(roc_problem::can::Problem::UnusedModuleImport(
|
|
||||||
unused, region,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unused, region) in unused_imports.drain() {
|
|
||||||
existing.push(roc_problem::can::Problem::UnusedImport(unused, region));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extend_module_with_builtin_import(module: &mut ParsedModule, module_id: ModuleId) {
|
fn extend_module_with_builtin_import(module: &mut ParsedModule, module_id: ModuleId) {
|
||||||
module
|
module
|
||||||
.package_qualified_imported_modules
|
.package_qualified_imported_modules
|
||||||
|
@ -2514,8 +2476,6 @@ fn update<'a>(
|
||||||
state.module_cache.documentation.insert(module_id, docs);
|
state.module_cache.documentation.insert(module_id, docs);
|
||||||
}
|
}
|
||||||
|
|
||||||
report_unused_imported_modules(&mut state, module_id, &constrained_module);
|
|
||||||
|
|
||||||
state
|
state
|
||||||
.module_cache
|
.module_cache
|
||||||
.aliases
|
.aliases
|
||||||
|
@ -5145,7 +5105,6 @@ fn canonicalize_and_constrain<'a>(
|
||||||
exposed_imports: module_output.exposed_imports,
|
exposed_imports: module_output.exposed_imports,
|
||||||
exposed_symbols: module_output.exposed_symbols,
|
exposed_symbols: module_output.exposed_symbols,
|
||||||
referenced_values: module_output.referenced_values,
|
referenced_values: module_output.referenced_values,
|
||||||
referenced_types: module_output.referenced_types,
|
|
||||||
aliases,
|
aliases,
|
||||||
rigid_variables: module_output.rigid_variables,
|
rigid_variables: module_output.rigid_variables,
|
||||||
abilities_store: module_output.scope.abilities_store,
|
abilities_store: module_output.scope.abilities_store,
|
||||||
|
|
|
@ -3,7 +3,7 @@ interface Primary
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
import Dep1
|
import Dep1
|
||||||
import Dep2 exposing [two]
|
import Dep2
|
||||||
import Dep3Blah exposing [bar]
|
import Dep3Blah exposing [bar]
|
||||||
import Res
|
import Res
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ interface Primary
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
import Dep1
|
import Dep1
|
||||||
import Dep2 exposing [two]
|
import Dep2
|
||||||
import Dep3 exposing [bar]
|
import Dep3 exposing [bar]
|
||||||
import Res
|
import Res
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ interface WithBuiltins
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
import Dep1
|
import Dep1
|
||||||
import Dep2 exposing [two]
|
import Dep2
|
||||||
|
|
||||||
floatTest = Num.maxF64
|
floatTest = Num.maxF64
|
||||||
|
|
||||||
|
|
|
@ -217,10 +217,21 @@ fn load_fixture(
|
||||||
|
|
||||||
let home = loaded_module.module_id;
|
let home = loaded_module.module_id;
|
||||||
|
|
||||||
assert_eq!(
|
let (filepath, src) = loaded_module.sources.get(&home).unwrap();
|
||||||
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
let can_problems = loaded_module.can_problems.remove(&home).unwrap_or_default();
|
||||||
Vec::new()
|
if !can_problems.is_empty() {
|
||||||
|
panic!(
|
||||||
|
"{}",
|
||||||
|
format_can_problems(
|
||||||
|
can_problems,
|
||||||
|
home,
|
||||||
|
&loaded_module.interns,
|
||||||
|
filepath.clone(),
|
||||||
|
src,
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
assert!(loaded_module
|
assert!(loaded_module
|
||||||
.type_problems
|
.type_problems
|
||||||
.remove(&home)
|
.remove(&home)
|
||||||
|
@ -453,7 +464,7 @@ fn import_inside_def() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "LookupNotInScope")]
|
#[should_panic(expected = "UNRECOGNIZED NAME")]
|
||||||
fn exposed_used_outside_scope() {
|
fn exposed_used_outside_scope() {
|
||||||
let subs_by_module = Default::default();
|
let subs_by_module = Default::default();
|
||||||
load_fixture(
|
load_fixture(
|
||||||
|
@ -464,7 +475,7 @@ fn exposed_used_outside_scope() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "ModuleNotImported")]
|
#[should_panic(expected = "MODULE NOT IMPORTED")]
|
||||||
fn import_used_outside_scope() {
|
fn import_used_outside_scope() {
|
||||||
let subs_by_module = Default::default();
|
let subs_by_module = Default::default();
|
||||||
load_fixture(
|
load_fixture(
|
||||||
|
@ -915,9 +926,9 @@ fn opaque_wrapped_unwrapped_outside_defining_module() {
|
||||||
|
|
||||||
Note: Opaque types can only be wrapped and unwrapped in the module they are defined in!
|
Note: Opaque types can only be wrapped and unwrapped in the module they are defined in!
|
||||||
|
|
||||||
── UNUSED IMPORT ─── tmp/opaque_wrapped_unwrapped_outside_defining_module/Main ─
|
── UNUSED IMPORT in tmp/opaque_wrapped_unwrapped_outside_defining_module/Main ──
|
||||||
|
|
||||||
Nothing from Age is used in this module.
|
Age is imported but not used.
|
||||||
|
|
||||||
3│ import Age exposing [Age]
|
3│ import Age exposing [Age]
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -930,6 +941,114 @@ fn opaque_wrapped_unwrapped_outside_defining_module() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unused_imports() {
|
||||||
|
let modules = vec![
|
||||||
|
(
|
||||||
|
"Dep1",
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
interface Dep1 exposes [one] imports []
|
||||||
|
one = 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Dep2",
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
interface Dep2 exposes [two] imports []
|
||||||
|
two = 2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Dep3",
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
interface Dep3 exposes [Three, three] imports []
|
||||||
|
|
||||||
|
Three : [Three]
|
||||||
|
|
||||||
|
three = Three
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Main",
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
interface Main exposes [usedModule, unusedModule, unusedExposed, usingThreeValue] imports []
|
||||||
|
|
||||||
|
import Dep1
|
||||||
|
import Dep3 exposing [Three]
|
||||||
|
|
||||||
|
usedModule =
|
||||||
|
import Dep2
|
||||||
|
Dep2.two
|
||||||
|
|
||||||
|
unusedModule =
|
||||||
|
import Dep2
|
||||||
|
2
|
||||||
|
|
||||||
|
unusedExposed =
|
||||||
|
import Dep2 exposing [two]
|
||||||
|
2
|
||||||
|
|
||||||
|
usingThreeValue =
|
||||||
|
Dep3.three
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
let err = multiple_modules("unused_imports", modules).unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
err,
|
||||||
|
indoc!(
|
||||||
|
r"
|
||||||
|
── UNUSED IMPORT in tmp/unused_imports/Main ────────────────────────────────────
|
||||||
|
|
||||||
|
Dep2 is imported but not used.
|
||||||
|
|
||||||
|
11│ import Dep2
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
Since Dep2 isn't used, you don't need to import it.
|
||||||
|
|
||||||
|
── UNUSED IMPORT in tmp/unused_imports/Main ────────────────────────────────────
|
||||||
|
|
||||||
|
Dep2 is imported but not used.
|
||||||
|
|
||||||
|
15│ import Dep2 exposing [two]
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Since Dep2 isn't used, you don't need to import it.
|
||||||
|
|
||||||
|
── UNUSED IMPORT in tmp/unused_imports/Main ────────────────────────────────────
|
||||||
|
|
||||||
|
Dep1 is imported but not used.
|
||||||
|
|
||||||
|
3│ import Dep1
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
Since Dep1 isn't used, you don't need to import it.
|
||||||
|
|
||||||
|
── UNUSED IMPORT in tmp/unused_imports/Main ────────────────────────────────────
|
||||||
|
|
||||||
|
`Dep3.Three` is not used in this module.
|
||||||
|
|
||||||
|
4│ import Dep3 exposing [Three]
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
Since `Dep3.Three` isn't used, you don't need to import it.
|
||||||
|
"
|
||||||
|
),
|
||||||
|
"\n{}",
|
||||||
|
err
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn issue_2863_module_type_does_not_exist() {
|
fn issue_2863_module_type_does_not_exist() {
|
||||||
let modules = vec![
|
let modules = vec![
|
||||||
|
|
|
@ -107,9 +107,8 @@ pub fn can_problem<'b>(
|
||||||
Problem::UnusedModuleImport(module_id, region) => {
|
Problem::UnusedModuleImport(module_id, region) => {
|
||||||
doc = alloc.stack([
|
doc = alloc.stack([
|
||||||
alloc.concat([
|
alloc.concat([
|
||||||
alloc.reflow("Nothing from "),
|
|
||||||
alloc.module(module_id),
|
alloc.module(module_id),
|
||||||
alloc.reflow(" is used in this module."),
|
alloc.reflow(" is imported but not used."),
|
||||||
]),
|
]),
|
||||||
alloc.region(lines.convert_region(region)),
|
alloc.region(lines.convert_region(region)),
|
||||||
alloc.concat([
|
alloc.concat([
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue