Skip to content

Commit 22ddb23

Browse files
committed
feat: add analysis_costs test for handling undefined variable
1 parent 2eec312 commit 22ddb23

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

clarity/src/vm/analysis/type_checker/v2_05/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,13 @@ impl<'a, 'b> TypeChecker<'a, 'b> {
706706
} else if let Some(type_result) = context.lookup_trait_reference_type(name) {
707707
Ok(TypeSignature::TraitReferenceType(type_result.clone()))
708708
} else {
709+
// Special case where a top-level is being looked up, which must
710+
// be undefined. This early error prevents a cost function error
711+
// due to `context.depth` being 0.
712+
if context.depth == 0 {
713+
return Err(CheckErrors::UndefinedVariable(name.to_string()).into());
714+
}
715+
709716
runtime_cost(
710717
ClarityCostFunction::AnalysisLookupVariableDepth,
711718
self,

stackslib/src/clarity_vm/tests/analysis_costs.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

1717
use clarity::vm::ast::ASTRules;
18-
use clarity::vm::clarity::TransactionConnection;
18+
use clarity::vm::clarity::{Error as ClarityError, TransactionConnection};
1919
use clarity::vm::costs::ExecutionCost;
20+
use clarity::vm::errors::CheckErrors;
2021
use clarity::vm::functions::NativeFunctions;
2122
use clarity::vm::test_util::TEST_HEADER_DB;
2223
use clarity::vm::tests::{test_only_mainnet_to_chain_id, UnitTestBurnStateDB};
2324
use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier, Value};
2425
use clarity::vm::{execute as vm_execute, ClarityVersion, ContractName};
26+
use rstest::rstest;
2527
use stacks_common::types::chainstate::StacksBlockId;
2628
use stacks_common::types::StacksEpochId;
2729

@@ -304,3 +306,49 @@ fn epoch_205_test_all_mainnet() {
304306
fn epoch_205_test_all_testnet() {
305307
epoch_205_test_all(false);
306308
}
309+
310+
#[rstest]
311+
#[case(true, StacksEpochId::Epoch21)]
312+
#[case(true, StacksEpochId::Epoch2_05)]
313+
#[case(false, StacksEpochId::Epoch21)]
314+
#[case(false, StacksEpochId::Epoch2_05)]
315+
fn undefined_top_variable_error(#[case] use_mainnet: bool, #[case] epoch: StacksEpochId) {
316+
let mut clarity_instance =
317+
setup_tracked_cost_test(use_mainnet, epoch, ClarityVersion::Clarity1);
318+
let contract_self = "foo";
319+
320+
let self_contract_id =
321+
QualifiedContractIdentifier::local("undefined-top-variable-error").unwrap();
322+
323+
let burn_state_db = UnitTestBurnStateDB {
324+
epoch_id: epoch,
325+
ast_rules: ASTRules::PrecheckSize,
326+
};
327+
328+
{
329+
let mut conn = clarity_instance.begin_block(
330+
&StacksBlockId([3; 32]),
331+
&StacksBlockId([4; 32]),
332+
&TEST_HEADER_DB,
333+
&burn_state_db,
334+
);
335+
336+
conn.as_transaction(|conn| {
337+
let analysis_result = conn.analyze_smart_contract(
338+
&self_contract_id,
339+
ClarityVersion::Clarity1,
340+
&contract_self,
341+
ASTRules::PrecheckSize,
342+
);
343+
let Err(ClarityError::Analysis(check_error)) = analysis_result else {
344+
panic!("Bad analysis result: {:?}", &analysis_result);
345+
};
346+
let CheckErrors::UndefinedVariable(var_name) = check_error.err else {
347+
panic!("Bad analysis error: {:?}", &check_error);
348+
};
349+
assert_eq!(var_name, "foo".to_string());
350+
});
351+
352+
conn.commit_block();
353+
}
354+
}

0 commit comments

Comments
 (0)