Skip to content
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

Lint for unnecessary type casts #11135

Closed
wants to merge 3 commits into from
Closed

Conversation

fhahn
Copy link
Contributor

@fhahn fhahn commented Dec 25, 2013

This patch adds a lint for unnecessary casts and removes a couple of unnecessary casts as mentioned in the discussion of #7313.

The main problem I have with this lint is handling macros. Some macros contain typecasts, which are necessary for most expansions, but for some expansion they are unnecessary. This leads to a lot of, probably useless, lint warnings (/errors) during compilation.
I found no way to find out, if nodes in the ast were expanded from a macro, to disable type cast checks for macros. Is there a way to find out, if an expression was expanded from a macro during linting?

@alexcrichton
Copy link
Member

This seems like an interesting lint, but in the FFI case I can imagine that this will warn about completely legitimate casts. The corresponding FFI types for integers in C often coincide with the current architecture's uint or int, but it may not if you cross compile for another architecture. This means in one mode of compilation the cast will be warned about but in the other it isn't.

In general I feel like lints should be encouraging you to improve your program or catching possible bugs, but this seem like a regular thing in some circumstances that shouldn't be warned about.

@emberian
Copy link
Member

Isn't that what #[allow] is for, though?

@fhahn
Copy link
Contributor Author

fhahn commented Dec 25, 2013

I think the problem @alexcrichton mentioned is similar to the problem related to macros. I think it would be nice to have an option to ignore typecasts in macros, although I don't have an idea how to do it at the moment. Does the AST, that's used by the linter, contain any information about expanded macros? As far as I understand, the macros are expanded at an earlier stage and the macro nodes are dropped afterwards.

For the FFI case, maybe an option to ignore typecasts with libc types would be useful?

Or we could simply use #[allow]. This would be the easiest solution, although it would only possible to use it at function or a higher compilation unit level, if I'm not mistaken.

@alexcrichton
Copy link
Member

This isn't even in macros, for example casting to size_t may be warned about on some systems but not warned about on others. I do not believe that regular programs should have allow attributes in them. The attribute syntax isn't exactly very sightly, and encouraging default usage of them (except for possibly the crate metadata attributes) is not the best route in my opinion.

I am worried that this will warn about enough legitimate casts that allow will start getting thrown around making is less useful in the first place anyway.

@emberian
Copy link
Member

That's true

On Wed, Dec 25, 2013 at 7:13 PM, Alex Crichton notifications@github.comwrote:

This isn't even in macros, for example casting to size_t may be warned
about on some systems but not warned about on others. I do not believe that
regular programs should have allow attributes in them. The attribute
syntax isn't exactly very sightly, and encouraging default usage of them
(except for possibly the crate metadata attributes) is not the best route
in my opinion.

I am worried that this will warn about enough legitimate casts that allowwill start getting thrown around making is less useful in the first place
anyway.


Reply to this email directly or view it on GitHubhttps://github.com//pull/11135#issuecomment-31207546
.

@fhahn
Copy link
Contributor Author

fhahn commented Dec 30, 2013

I've added tests for the three cases mentioned in the discussion above:

During the last couple of days, I had a look into possible solutions for the problems mentioned above.
To avoid cast checks for ffi types (the types from libc), I use the type name used for the cast (https://github.com/mozilla/rust/pull/11135/files#diff-1e2a5771a9e8a30cc0802838a4a9da9dR609). I check if the last part of the type name is one of the libc type names (at the moment I check only, if the type is size_t, but all other relevant types could be checked using a hashmap). This isn't the cleanest solution, because it only uses the last part of the type string, but I didn't find another way to check if the type is a ffi type.

To skip cast checks in expanded macros I added a boolean flag the ExprCast, which is set to true for ExprCast nodes of an expanded macro. This is probably a very big change for a simple thing like a lint, but I found no other way to check, if an AST node is part of an expanded macro. If there is a better way, I would be happy to use it.

Both fixes probably miss a couple of unnecessay casts, e.g. if someone uses their own type with a name like size_t or something like

let x: u64 = x;
let y: u64 = x as u64;

in a macro. This patch will probably require more work, but I would like to get some feedback about the direction of the patch.

@alexcrichton
Copy link
Member

I'm personally not a fan of singling out libc or size_t specifically from a lint like this. Compiler internals should generally work with a generic set of rules or else we run into problems like LLVM only knowing how to optimize functions literally called "malloc" and "free".

I personally think that for this lint to be useful it will require a lot of special casing that probably shouldn't be in the compiler anyway, but it also may just need a different direction to go in.

@fhahn
Copy link
Contributor Author

fhahn commented Jan 4, 2014

I agree this isn't a viable solution. It was intended as quick proof of concept, but at the moment I do not see a way to special case the libc types. Therefore it would be necessary to check if a type is platform depended.

But I managed to lint libextra and libstd and found a couple of unnecessary typecasts. I reviewed them and pushed them to a separate branch.

How should we move on with this pull request? Should I close it?
I could submit another PR for the removal of the unnecessary typecasts, if this cleanup is wanted.

@emberian
Copy link
Member

emberian commented Jan 5, 2014

Could this lint default to allow? I still think it's a useful analysis to
have.

On Sat, Jan 4, 2014 at 5:57 PM, Florian Hahn notifications@github.comwrote:

I agree this isn't a viable solution. It was intended as quick proof of
concept, but at the moment I do not see a way to special case the libc
types. Therefore it would be necessary to check if a type is platform
depended.

But I managed to lint libextra and libstd and found a couple of
unnecessary typecasts. I reviewed them and pushed them to a separate
branchhttps://github.com/fhahn/rust/commit/821b3a83e8e7270d981d45303a7578089537f325
.

How should we move on with this pull request? Should I close it?
I could submit another PR for the removal of the unnecessary typecasts, if
this cleanup is wanted.


Reply to this email directly or view it on GitHubhttps://github.com//pull/11135#issuecomment-31591003
.

@alexcrichton
Copy link
Member

I like @cmr's suggestion, but let's revert the changes to the AST and the parser for now. I would expect the lint to be entirely self contained in middle::lint, and then I'm more than happy to see all the unnecessary casts go away.

Conflicts:
	src/librustc/middle/lint.rs
@fhahn fhahn closed this Jan 5, 2014
@fhahn fhahn deleted the unused-cast-lint branch January 5, 2014 20:10
@fhahn fhahn restored the unused-cast-lint branch January 5, 2014 20:10
@fhahn fhahn deleted the unused-cast-lint branch January 5, 2014 20:14
@fhahn fhahn restored the unused-cast-lint branch January 5, 2014 20:14
@fhahn fhahn deleted the unused-cast-lint branch January 5, 2014 20:24
@fhahn fhahn restored the unused-cast-lint branch January 5, 2014 20:25
@fhahn
Copy link
Contributor Author

fhahn commented Jan 5, 2014

I've reverted the changes to the AST and the parser. Somehow I couldn't manage to overwrite the branch of this pull request (fhahn:unused-cast-lint) with the updated commits. I'm going to close this PR and create a new one (#11329) with the updates (Sorry for this inconvenience) .

bors added a commit that referenced this pull request Jan 7, 2014
flip1995 pushed a commit to flip1995/rust that referenced this pull request Jul 31, 2023
…=Centri3

Fix `unwrap_or_else_default` false positive

This PR fixes a false positive in the handling of `unwrap_or_else` with a default value when the value is needed for type inference.

An easy example to exhibit the false positive is the following:
```rust
    let option = None;
    option.unwrap_or_else(Vec::new).push(1);
```
The following code would not compile, because the fact that the value is a `Vec` has been lost:
```rust
    let option = None;
    option.unwrap_or_default().push(1);
```
The fix is to:
- implement a heuristic to tell whether an expression's type can be determined purely from its subexpressions, and the arguments and locals they use;
- apply the heuristic to `unwrap_or_else`'s receiver.

The heuristic returns false when applied to `option` in the above example, but it returns true when applied to `option` in either of the following examples:
```rust
    let option: Option<Vec<u64>> = None;
    option.unwrap_or_else(Vec::new).push(1);
```
```rust
    let option = None::<Vec<u64>>;
    option.unwrap_or_else(Vec::new).push(1);
```

(Aside: rust-lang/rust-clippy#10120 unfairly contained multiple changes in one PR. I am trying to break that PR up into smaller pieces.)

---

changelog: FP: [`unwrap_or_else_default`]: No longer lints if the default value is needed for type inference
flip1995 pushed a commit to flip1995/rust that referenced this pull request Apr 18, 2024
type certainty: clear `DefId` when an expression's type changes to non-adt

Fixes rust-lang#12585

The root cause of the ICE in the linked issue was in the expression `one.x`, in the array literal.

The type of `one` is the `One` struct: an adt with a DefId, so its certainty is `Certain(def_id_of_one)`. However, the field access `.x` can then change the type (to `i32` here) and that should update that `DefId` accordingly. It does do that correctly when `one.x` would be another adt with a DefId:

https://github.com/rust-lang/rust-clippy/blob/97ba291d5aa026353ad93e48cf00e06f08c73830/clippy_utils/src/ty/type_certainty/mod.rs#L90-L91

but when it *isn't* an adt and there is no def id (which is the case in the linked issue: `one.x` is an i32), it keeps the `DefId` of `One`, even though that's the wrong type (which would then lead to a contradiction later when joining `Certainty`s):
https://github.com/rust-lang/rust-clippy/blob/97ba291d5aa026353ad93e48cf00e06f08c73830/clippy_utils/src/ty/type_certainty/mod.rs#L92-L93

In particular, in the linked issue, `from_array([one.x, two.x])` would try to join the `Certainty` of the two array elements, which *should* have been `[Certain(None), Certain(None)]`, because `i32`s have no `DefId`, but instead it was `[Certain(One), Certain(Two)]`, because the DefId wasn't cleared from when it was visiting `one` and `two`. This is the "contradiction" that could be seen in the ICE message

... so this changes it to clear the `DefId` when it isn't an adt.

cc `@smoelius` you implemented this initially in rust-lang#11135, does this change make sense to you?

changelog: none
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants