Skip to content

Commit

Permalink
added validations for traitItemInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
dean-starkware committed Feb 7, 2025
1 parent d565dfd commit 84d0cba
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 27 deletions.
29 changes: 29 additions & 0 deletions crates/cairo-lang-semantic/src/diagnostic_test_data/tests
Original file line number Diff line number Diff line change
Expand Up @@ -1267,3 +1267,32 @@ error: Global `use` item is not supported in `V2023_01` edition.
--> lib.cairo:4:8
use a::*;
^

//! > ==========================================================================

//! > Test usage of deprecated trait function.

//! > test_runner_name
test_expr_diagnostics(expect_diagnostics: warnings_only)

//! > expr_code
{
let x = Struct {};
x.deprecated_method();
}

//! > module_code
pub struct Struct {}

pub trait MyTrait {
#[deprecated(feature: "deprecated", note: "This method is deprecated", since: "1.0.0")]
fn deprecated_method(self: @Struct);
}

impl MyTraitImpl of MyTrait {
fn deprecated_method(self: @Struct) {}
}

//! > function_body

//! > expected_diagnostics
6 changes: 6 additions & 0 deletions crates/cairo-lang-semantic/src/items/feature_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ impl FeatureKind {
}
}

/// A trait for retrieving the `FeatureKind` of an item.
pub trait GetFeatureKind {
/// Returns the `FeatureKind` associated with the implementing struct.
fn get_feature_kind(&self) -> FeatureKind;
}

/// Diagnostics for feature markers.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum FeatureMarkerDiagnostic {
Expand Down
7 changes: 7 additions & 0 deletions crates/cairo-lang-semantic/src/items/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use super::visibility::{Visibility, peek_visible_in};
use crate::SemanticDiagnostic;
use crate::db::{SemanticGroup, get_resolver_data_options};
use crate::diagnostic::{SemanticDiagnosticKind, SemanticDiagnosticsBuilder};
use crate::items::feature_kind::GetFeatureKind;
use crate::resolve::ResolvedGenericItem;

/// Information per item in a module.
Expand Down Expand Up @@ -284,3 +285,9 @@ fn specific_module_usable_trait_ids(
}
Ok(module_traits)
}

impl GetFeatureKind for ModuleItemInfo {
fn get_feature_kind(&self) -> FeatureKind {
self.feature_kind.clone()
}
}
70 changes: 60 additions & 10 deletions crates/cairo-lang-semantic/src/items/trt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use cairo_lang_utils::{Intern, LookupIntern, define_short_id};
use smol_str::SmolStr;

use super::TraitOrImplContext;
use super::feature_kind::FeatureKind;
use super::function_with_body::{FunctionBodyData, get_implicit_precedence, get_inline_config};
use super::functions::{
FunctionDeclarationData, GenericFunctionId, ImplGenericFunctionId, ImplicitPrecedence,
Expand All @@ -36,6 +37,7 @@ use crate::diagnostic::{NotFoundItemType, SemanticDiagnostics, SemanticDiagnosti
use crate::expr::compute::{ComputationContext, ContextFunction, Environment, compute_root_expr};
use crate::expr::inference::InferenceId;
use crate::expr::inference::canonic::ResultNoErrEx;
use crate::items::feature_kind::GetFeatureKind;
use crate::resolve::{ResolvedConcreteItem, Resolver, ResolverData};
use crate::substitution::{GenericSubstitution, SemanticRewriter, SubstitutionRewriter};
use crate::types::resolve_type;
Expand Down Expand Up @@ -477,7 +479,29 @@ pub struct TraitDefinitionData {
item_impl_asts: OrderedHashMap<TraitImplId, ast::TraitItemImpl>,

/// Mapping of item names to their IDs. All the IDs should appear in one of the AST maps above.
item_id_by_name: Arc<OrderedHashMap<SmolStr, TraitItemId>>,
item_id_by_name: Arc<OrderedHashMap<SmolStr, TraitItemInfo>>,
}

impl TraitDefinitionData {
/// Retrieves trait item information by its name.
pub fn get_trait_item_info(&self, item_name: &SmolStr) -> Option<TraitItemInfo> {
self.item_id_by_name.get(item_name).cloned()
}
}
/// Stores metadata for a trait item, including its ID and feature kind.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TraitItemInfo {
/// The unique identifier of the trait item.
pub id: TraitItemId,
/// The feature kind associated with this trait item.
pub feature_kind: FeatureKind,
}

impl GetFeatureKind for TraitItemInfo {
/// Returns the feature kind of this trait item.
fn get_feature_kind(&self) -> FeatureKind {
self.feature_kind.clone()
}
}

// --- Selectors ---
Expand Down Expand Up @@ -520,7 +544,7 @@ pub fn trait_required_item_names(
) -> Maybe<OrderedHashSet<SmolStr>> {
let mut required_items = OrderedHashSet::<_>::default();
for (item_name, item_id) in db.priv_trait_definition_data(trait_id)?.item_id_by_name.iter() {
if match item_id {
if match item_id.id {
TraitItemId::Function(id) => {
let body = id.stable_ptr(db.upcast()).lookup(db.upcast()).body(db.upcast());
matches!(body, ast::MaybeTraitFunctionBody::None(_))
Expand All @@ -540,7 +564,7 @@ pub fn trait_item_by_name(
trait_id: TraitId,
name: SmolStr,
) -> Maybe<Option<TraitItemId>> {
Ok(db.priv_trait_definition_data(trait_id)?.item_id_by_name.get(&name).cloned())
Ok(db.priv_trait_definition_data(trait_id)?.item_id_by_name.get(&name).map(|info| info.id))
}

/// Query implementation of [SemanticGroup::trait_all_used_items].
Expand All @@ -551,7 +575,7 @@ pub fn trait_all_used_items(
let mut all_used_items = db.trait_resolver_data(trait_id)?.used_items.clone();
let data = db.priv_trait_definition_data(trait_id)?;
for item in data.item_id_by_name.values() {
for resolver_data in get_resolver_data_options(LookupItemId::TraitItem(*item), db) {
for resolver_data in get_resolver_data_options(LookupItemId::TraitItem(item.id), db) {
all_used_items.extend(resolver_data.used_items.iter().cloned());
}
}
Expand Down Expand Up @@ -679,7 +703,7 @@ pub fn priv_trait_definition_data(
let mut item_type_asts = OrderedHashMap::default();
let mut item_constant_asts = OrderedHashMap::default();
let mut item_impl_asts = OrderedHashMap::default();
let mut item_id_by_name = OrderedHashMap::default();
let mut item_id_by_name: OrderedHashMap<SmolStr, TraitItemInfo> = OrderedHashMap::default();

if let ast::MaybeTraitBody::Some(body) = trait_ast.body(syntax_db) {
for item in body.items(syntax_db).elements(syntax_db) {
Expand All @@ -689,8 +713,14 @@ pub fn priv_trait_definition_data(
TraitFunctionLongId(module_file_id, func.stable_ptr()).intern(db);
let name_node = func.declaration(syntax_db).name(syntax_db);
let name = name_node.text(syntax_db);
let attributes = func.attributes(syntax_db);
let feature_kind =
FeatureKind::from_ast(db.upcast(), &mut diagnostics, &attributes);
if item_id_by_name
.insert(name.clone(), TraitItemId::Function(trait_func_id))
.insert(name.clone(), TraitItemInfo {
id: TraitItemId::Function(trait_func_id),
feature_kind,
})
.is_some()
{
diagnostics.report(
Expand All @@ -704,8 +734,14 @@ pub fn priv_trait_definition_data(
let trait_type_id = TraitTypeLongId(module_file_id, ty.stable_ptr()).intern(db);
let name_node = ty.name(syntax_db);
let name = name_node.text(syntax_db);
let attributes = ty.attributes(syntax_db);
let feature_kind =
FeatureKind::from_ast(db.upcast(), &mut diagnostics, &attributes);
if item_id_by_name
.insert(name.clone(), TraitItemId::Type(trait_type_id))
.insert(name.clone(), TraitItemInfo {
id: TraitItemId::Type(trait_type_id),
feature_kind,
})
.is_some()
{
diagnostics.report(
Expand All @@ -721,8 +757,14 @@ pub fn priv_trait_definition_data(

let name_node = constant.name(syntax_db);
let name = name_node.text(syntax_db);
let attributes = constant.attributes(syntax_db);
let feature_kind =
FeatureKind::from_ast(db.upcast(), &mut diagnostics, &attributes);
if item_id_by_name
.insert(name.clone(), TraitItemId::Constant(trait_constant))
.insert(name.clone(), TraitItemInfo {
id: TraitItemId::Constant(trait_constant),
feature_kind,
})
.is_some()
{
diagnostics.report(
Expand All @@ -737,7 +779,15 @@ pub fn priv_trait_definition_data(

let name_node = imp.name(syntax_db);
let name = name_node.text(syntax_db);
if item_id_by_name.insert(name.clone(), TraitItemId::Impl(trait_impl)).is_some()
let attributes = imp.attributes(syntax_db);
let feature_kind =
FeatureKind::from_ast(db.upcast(), &mut diagnostics, &attributes);
if item_id_by_name
.insert(name.clone(), TraitItemInfo {
id: TraitItemId::Impl(trait_impl),
feature_kind,
})
.is_some()
{
diagnostics.report(
&name_node,
Expand All @@ -757,7 +807,7 @@ pub fn priv_trait_definition_data(
item_type_asts,
item_constant_asts,
item_impl_asts,
item_id_by_name: item_id_by_name.into(),
item_id_by_name: Arc::new(item_id_by_name),
})
}

Expand Down
83 changes: 66 additions & 17 deletions crates/cairo-lang-semantic/src/resolve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ use crate::expr::inference::infers::InferenceEmbeddings;
use crate::expr::inference::{Inference, InferenceData, InferenceId};
use crate::items::constant::{ConstValue, ImplConstantId, resolve_const_expr_and_evaluate};
use crate::items::enm::SemanticEnumEx;
use crate::items::feature_kind::{FeatureConfig, FeatureKind, extract_feature_config};
use crate::items::feature_kind::{
FeatureConfig, FeatureKind, GetFeatureKind, extract_feature_config,
};
use crate::items::functions::{GenericFunctionId, ImplGenericFunctionId};
use crate::items::generics::generic_params_to_args;
use crate::items::imp::{
Expand Down Expand Up @@ -420,7 +422,7 @@ impl<'db> Resolver<'db> {
) -> Maybe<ResolvedConcreteItem> {
let generic_args_syntax = segment.generic_args(self.db.upcast());
let segment_stable_ptr = segment.stable_ptr().untyped();
self.validate_item_usability(diagnostics, module_id, identifier, &inner_item_info);
self.validate_module_item_usability(diagnostics, module_id, identifier, &inner_item_info);
self.data.used_items.insert(LookupItemId::ModuleItem(inner_item_info.item_id));
let inner_generic_item =
ResolvedGenericItem::from_module_item(self.db, inner_item_info.item_id)?;
Expand Down Expand Up @@ -839,11 +841,20 @@ impl<'db> Resolver<'db> {
}
ResolvedConcreteItem::SelfTrait(concrete_trait_id) => {
let impl_id = ImplLongId::SelfImpl(*concrete_trait_id).intern(self.db);
let Some(trait_item_id) =
self.db.trait_item_by_name(concrete_trait_id.trait_id(self.db), ident)?
let Some(trait_item_id) = self
.db
.trait_item_by_name(concrete_trait_id.trait_id(self.db), ident.clone())?
else {
return Err(diagnostics.report(identifier, InvalidPath));
};
if let Ok(trait_definition_data) =
self.db.priv_trait_definition_data(concrete_trait_id.trait_id(self.db))
{
if let Some(trait_item_info) = trait_definition_data.get_trait_item_info(&ident)
{
self.validate_item_usability(diagnostics, identifier, &trait_item_info);
}
}
self.data.used_items.insert(LookupItemId::TraitItem(trait_item_id));
Ok(match trait_item_id {
TraitItemId::Function(trait_function_id) => {
Expand Down Expand Up @@ -876,11 +887,21 @@ impl<'db> Resolver<'db> {
})
}
ResolvedConcreteItem::Trait(concrete_trait_id) => {
let Some(trait_item_id) =
self.db.trait_item_by_name(concrete_trait_id.trait_id(self.db), ident)?
let Some(trait_item_id) = self
.db
.trait_item_by_name(concrete_trait_id.trait_id(self.db), ident.clone())?
else {
return Err(diagnostics.report(identifier, InvalidPath));
};

if let Ok(trait_definition_data) =
self.db.priv_trait_definition_data(concrete_trait_id.trait_id(self.db))
{
if let Some(trait_item_info) = trait_definition_data.get_trait_item_info(&ident)
{
self.validate_item_usability(diagnostics, identifier, &trait_item_info);
}
}
self.data.used_items.insert(LookupItemId::TraitItem(trait_item_id));

match trait_item_id {
Expand Down Expand Up @@ -973,9 +994,18 @@ impl<'db> Resolver<'db> {
ResolvedConcreteItem::Impl(impl_id) => {
let concrete_trait_id = self.db.impl_concrete_trait(*impl_id)?;
let trait_id = concrete_trait_id.trait_id(self.db);
let Some(trait_item_id) = self.db.trait_item_by_name(trait_id, ident)? else {
let Some(trait_item_id) = self.db.trait_item_by_name(trait_id, ident.clone())?
else {
return Err(diagnostics.report(identifier, InvalidPath));
};
if let Ok(trait_definition_data) =
self.db.priv_trait_definition_data(concrete_trait_id.trait_id(self.db))
{
if let Some(trait_item_info) = trait_definition_data.get_trait_item_info(&ident)
{
self.validate_item_usability(diagnostics, identifier, &trait_item_info);
}
}
self.data.used_items.insert(LookupItemId::TraitItem(trait_item_id));

match trait_item_id {
Expand Down Expand Up @@ -1217,7 +1247,12 @@ impl<'db> Resolver<'db> {
item_type,
)?;

self.validate_item_usability(diagnostics, *module_id, identifier, &inner_item_info);
self.validate_module_item_usability(
diagnostics,
*module_id,
identifier,
&inner_item_info,
);
self.data.used_items.insert(LookupItemId::ModuleItem(inner_item_info.item_id));
ResolvedGenericItem::from_module_item(self.db, inner_item_info.item_id)
}
Expand Down Expand Up @@ -1707,19 +1742,17 @@ impl<'db> Resolver<'db> {
|| self.settings.edition.ignore_visibility() && module_crate == self.db.core_crate()
}

/// Validates that an item is usable from the current module or adds a diagnostic.
/// This includes visibility checks and feature checks.
fn validate_item_usability(
/// Validates whether a given item is allowed based on its feature kind.
/// This function checks if the item's feature kind is allowed in the current
/// configuration. If the item uses an unstable, deprecated, or internal feature
/// that is not permitted, a corresponding diagnostic error is reported.
fn validate_item_usability<T: GetFeatureKind>(
&self,
diagnostics: &mut SemanticDiagnostics,
containing_module_id: ModuleId,
identifier: &ast::TerminalIdentifier,
item_info: &ModuleItemInfo,
item_info: &T,
) {
if !self.is_item_visible(containing_module_id, item_info, self.module_file_id.0) {
diagnostics.report(identifier, ItemNotVisible(item_info.item_id, vec![]));
}
match &item_info.feature_kind {
match &item_info.get_feature_kind() {
FeatureKind::Unstable { feature, note }
if !self.data.feature_config.allowed_features.contains(feature) =>
{
Expand Down Expand Up @@ -1749,6 +1782,22 @@ impl<'db> Resolver<'db> {
}
}

/// Validates that an item is usable from the current module or adds a diagnostic.
/// This includes visibility checks and feature checks.
fn validate_module_item_usability(
&self,
diagnostics: &mut SemanticDiagnostics,
containing_module_id: ModuleId,
identifier: &ast::TerminalIdentifier,
item_info: &ModuleItemInfo,
) {
if !self.is_item_visible(containing_module_id, item_info, self.module_file_id.0) {
diagnostics.report(identifier, ItemNotVisible(item_info.item_id, vec![]));
}

self.validate_item_usability(diagnostics, identifier, item_info);
}

/// Checks if an item is visible from the current module.
fn is_item_visible(
&self,
Expand Down

0 comments on commit 84d0cba

Please sign in to comment.