-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat(lint): add UnsafeTypecast lint #11046
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat(lint): add UnsafeTypecast lint #11046
Conversation
warning[unsafe-typecast]: typecasts that can truncate values should be avoided | ||
--> ROOT/testdata/UnsafeTypecast.sol:LL:CC | ||
| | ||
5 | uint128 b = uint128(a); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uint128 -> uint128 is safe
uintN -> uintN is always safe
intN -> intN is always safe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a is an address
type so i think it still unsafe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely ensure the compiler isn't catching some of these, cannot directly convert an address to uint128 without a compile error.
warning[unsafe-typecast]: typecasts that can truncate values should be avoided | ||
--> ROOT/testdata/UnsafeTypecast.sol:LL:CC | ||
| | ||
94 | uint128 a = uint128(1000000000000000000000000000000000000000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure the compiler prevents you from downcasting a literal that's smaller than the target type size.
warning[unsafe-typecast]: typecasts that can truncate values should be avoided | ||
--> ROOT/testdata/UnsafeTypecast.sol:LL:CC | ||
| | ||
8 | uint64 c = uint64(a); | ||
| --------- | ||
| | ||
= help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any updates, can update the tests for you if needed @TropicalDog17 |
…foundry into feat/unsafe-typecast-lint
Hi @0xClandestine, I've updated the test, but not sure how to emit a helpful fix for the lint, can you help me on that? Thank a lot! |
maybe in this case a warning is enough, and no fix suggestion is required, but you could tell them to consider adding an explicit check like: if (a > type(uint32).max) revert TypecastOverflow(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please address the feedback
solar_ast::LitKind::Number(num) => { | ||
if is_negative_number_literal(num) { | ||
Some(hir::ElementaryType::Int(solar_ast::TypeSize::ZERO)) | ||
} else { | ||
Some(hir::ElementaryType::UInt(solar_ast::TypeSize::ZERO)) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the compiler performs checks when assigning literal values, i think it is safe to remove this check, which would also remove the need of the bigint
dep.
Please return None
and add a comment explaining the reason why
Assigning values which cannot fit into the type gives a compiler error. For example:
uint8 foo = 300;
The largest value an uint8 can hold is (2 8) - 1 = 255. So, the compiler says:
value 300 does not fit into type uint8
ref: https://solang.readthedocs.io/en/latest/language/types.html
…foundry into feat/unsafe-typecast-lint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, some nits 🤝
= note: Consider disabling this lint only if you're certain the cast is safe: | ||
|
||
- // Ensure the value fits in the target type before casting | ||
- // forge-lint: disable-next-line(unsafe-typecast) | ||
- // Cast is safe because [explain why] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, some small nits.
- Let's make the snippet an addition rather than deletion.
- Minor changes to
note
and suggestion.
= note: Consider disabling this lint only if you're certain the cast is safe: | |
- // Ensure the value fits in the target type before casting | |
- // forge-lint: disable-next-line(unsafe-typecast) | |
- // Cast is safe because [explain why] | |
= note: Consider disabling this lint if you're certain the cast is safe: | |
+ // Cast is safe because [explain why] | |
+ // forge-lint: disable-next-line(unsafe-typecast) |
UNSAFE_TYPECAST, | ||
Severity::Med, | ||
"unsafe-typecast", | ||
"typecasts that can truncate values should be avoided" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"typecasts that can truncate values should be avoided" | |
"typecasts that can truncate values should be checked" |
desc: Some("Consider disabling this lint only if you're certain the cast is safe:"), | ||
code: "// Ensure the value fits in the target type before casting\n// forge-lint: disable-next-line(unsafe-typecast)\n// Cast is safe because [explain why]".to_string(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
desc: Some("Consider disabling this lint only if you're certain the cast is safe:"), | |
code: "// Ensure the value fits in the target type before casting\n// forge-lint: disable-next-line(unsafe-typecast)\n// Cast is safe because [explain why]".to_string(), | |
desc: Some("Consider disabling this lint if you're certain the cast is safe:"), | |
code: "// Cast is safe because [explain why]".to_string()\n// forge-lint: disable-next-line(unsafe-typecast), |
@@ -0,0 +1,219 @@ | |||
contract UnsafeTypecast { | |||
function upcastSafeUint() public pure { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mb, can we get a bytes32 version of this as well?
int248 E = int248(D); | ||
int256 F = int256(E); | ||
} | ||
function downcastUnsafeUint() public pure { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also get a bytes32 version of this?
Motivation
Closes #11029
Solution
PR Checklist