diff --git a/clarity/src/vm/analysis/errors.rs b/clarity/src/vm/analysis/errors.rs index 71fefb645..257d2e5bb 100644 --- a/clarity/src/vm/analysis/errors.rs +++ b/clarity/src/vm/analysis/errors.rs @@ -136,6 +136,7 @@ pub enum CheckErrors { GetBurnBlockInfoExpectPropertyName, NameAlreadyUsed(String), + ReservedWord(String), // expect a function, or applying a function to a list NonFunctionApplication, @@ -408,6 +409,7 @@ impl DiagnosableError for CheckErrors { CheckErrors::GetBlockInfoExpectPropertyName => "missing property name for block info introspection".into(), CheckErrors::GetBurnBlockInfoExpectPropertyName => "missing property name for burn block info introspection".into(), CheckErrors::NameAlreadyUsed(name) => format!("defining '{}' conflicts with previous value", name), + CheckErrors::ReservedWord(name) => format!("{name} is a reserved word"), CheckErrors::NonFunctionApplication => "expecting expression of type function".into(), CheckErrors::ExpectedListApplication => "expecting expression of type list".into(), CheckErrors::ExpectedSequence(found_type) => format!("expecting expression of type 'list', 'buff', 'string-ascii' or 'string-utf8' - found '{}'", found_type), diff --git a/clarity/src/vm/analysis/type_checker/mod.rs b/clarity/src/vm/analysis/type_checker/mod.rs index c8185dde7..800347d0f 100644 --- a/clarity/src/vm/analysis/type_checker/mod.rs +++ b/clarity/src/vm/analysis/type_checker/mod.rs @@ -84,3 +84,21 @@ impl FunctionType { } } } + +fn is_reserved_word_v3(word: &str) -> bool { + match word { + "block-height" => true, + _ => false, + } +} + +/// Is this a reserved word that should trigger an analysis error for the given +/// Clarity version? Note that most of the reserved words do not trigger an +/// analysis error, but will trigger an error at runtime. This should likely be +/// changed in a future Clarity version. +pub fn is_reserved_word(word: &str, version: ClarityVersion) -> bool { + match version { + ClarityVersion::Clarity1 | ClarityVersion::Clarity2 => false, + ClarityVersion::Clarity3 => is_reserved_word_v3(word), + } +} diff --git a/clarity/src/vm/analysis/type_checker/v2_1/contexts.rs b/clarity/src/vm/analysis/type_checker/v2_1/contexts.rs index cc72fb91d..c2b7a98eb 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/contexts.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/contexts.rs @@ -19,6 +19,7 @@ use std::collections::BTreeMap; use hashbrown::{HashMap, HashSet}; use crate::vm::analysis::errors::{CheckError, CheckErrors, CheckResult}; +use crate::vm::analysis::type_checker::is_reserved_word; use crate::vm::analysis::types::ContractAnalysis; use crate::vm::contexts::MAX_CONTEXT_DEPTH; use crate::vm::representations::{ClarityName, SymbolicExpression}; @@ -128,6 +129,7 @@ impl TraitContext { } pub struct ContractContext { + clarity_version: ClarityVersion, contract_identifier: QualifiedContractIdentifier, map_types: HashMap, variable_types: HashMap, @@ -147,6 +149,7 @@ impl ContractContext { clarity_version: ClarityVersion, ) -> ContractContext { ContractContext { + clarity_version, contract_identifier, variable_types: HashMap::new(), private_function_types: HashMap::new(), @@ -168,6 +171,10 @@ impl ContractContext { } pub fn check_name_used(&self, name: &str) -> CheckResult<()> { + if is_reserved_word(name, self.clarity_version) { + return Err(CheckError::new(CheckErrors::ReservedWord(name.to_string()))); + } + if self.variable_types.contains_key(name) || self.persisted_variable_types.contains_key(name) || self.private_function_types.contains_key(name)