fix: improve error in type checker

The error from the type checker when attempting to retrieve a trait type
was incorrect. If the current contract is "foo", and the type "bar.baz"
is not found in the analysis database, the error message said:

```
use of unresolved contract 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.foo'
```

When the actual problem was that the contract "bar" was not found.

Similarly, if the contract "bar" is found, but the trait "baz" is not
found, the same error was given, instead of correctly showing that the
trait "baz" was not found.

See #3064
This commit is contained in:
Brice Dobry
2022-03-01 11:41:52 -05:00
parent d82f467a9b
commit b94c815240
4 changed files with 46 additions and 2 deletions

View File

@@ -77,6 +77,8 @@ tests (#2989).
key in the cost estimator. (#2984)
- Fixed a few prometheus metrics to be more accurate compared to `/v2` endpoints
when polling data (#2987)
- Fixed an error message from the type-checker that shows up when the type of a
parameter refers to a trait defined in the same contract (#3064).
## [2.05.0.0.0]

View File

@@ -156,6 +156,7 @@ pub enum CheckErrors {
UnknownFunction(String),
// traits
NoSuchTrait(String, String),
TraitReferenceUnknown(String),
TraitMethodUnknown(String, String),
ExpectedTraitIdentifier,
@@ -393,6 +394,7 @@ impl DiagnosableError for CheckErrors {
CheckErrors::DefineNFTBadSignature => format!("(define-asset ...) expects an asset name and an asset identifier type signature as arguments"),
CheckErrors::NoSuchNFT(asset_name) => format!("tried to use asset function with a undefined asset ('{}')", asset_name),
CheckErrors::NoSuchFT(asset_name) => format!("tried to use token function with a undefined token ('{}')", asset_name),
CheckErrors::NoSuchTrait(contract_name, trait_name) => format!("use of unresolved trait {}.{}", contract_name, trait_name),
CheckErrors::TraitReferenceUnknown(trait_name) => format!("use of undeclared trait <{}>", trait_name),
CheckErrors::TraitMethodUnknown(trait_name, func_name) => format!("method '{}' unspecified in trait <{}>", func_name, trait_name),
CheckErrors::ImportTraitBadSignature => format!("(use-trait ...) expects a trait name and a trait identifier"),

View File

@@ -1288,3 +1288,38 @@ fn test_return_trait_with_contract_of_wrapped_in_let() {
})
.unwrap();
}
#[test]
fn test_trait_contract_not_found() {
let trait_contract_src = "(define-trait my-trait
((hello (int) (response uint uint)))
)
(define-private (pass-trait (a <my-trait>))
(print a)
)
(define-public (call-it)
(ok (pass-trait .impl-contract))
)";
let impl_contract_src = "(define-public (hello (a int))
(ok u0)
)";
let trait_contract_id = QualifiedContractIdentifier::local("trait-contract").unwrap();
let impl_contract_id = QualifiedContractIdentifier::local("impl-contract").unwrap();
let mut impl_contract = parse(&impl_contract_id, impl_contract_src).unwrap();
let mut trait_contract = parse(&trait_contract_id, trait_contract_src).unwrap();
let mut marf = MemoryBackingStore::new();
let mut db = marf.as_analysis_db();
let err = db
.execute(|db| {
type_check(&impl_contract_id, &mut impl_contract, db, true)?;
type_check(&trait_contract_id, &mut trait_contract, db, true)
})
.unwrap_err();
match err.err {
CheckErrors::NoSuchContract(contract) => assert!(contract.ends_with(".trait-contract")),
_ => panic!("{:?}", err),
}
}

View File

@@ -418,11 +418,16 @@ impl<'a, 'b> TypeChecker<'a, 'b> {
let contract_defining_trait = self
.db
.load_contract(&trait_identifier.contract_identifier)
.ok_or(CheckErrors::NoSuchContract(contract_identifier.to_string()))?;
.ok_or(CheckErrors::NoSuchContract(
trait_identifier.contract_identifier.to_string(),
))?;
let trait_definition = contract_defining_trait
.get_defined_trait(&trait_identifier.name)
.ok_or(CheckErrors::NoSuchContract(contract_identifier.to_string()))?;
.ok_or(CheckErrors::NoSuchTrait(
trait_identifier.contract_identifier.to_string(),
trait_identifier.name.to_string(),
))?;
contract_to_check.check_trait_compliance(trait_identifier, trait_definition)?;
return Ok(expected_type.clone());