Critiques - Temporaries read before written.

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Critiques - Temporaries read before written.

Ben Coman
The greater prominence of Critiques in Calypso 
encourages me to try to clean them out.

I bumped into a false positive "Temporaries read before written."
that I've condensed to the following example.

    test
        |x|
        [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]

Now before I log an Issue, is it feasible to be able to recognise this?
Perhaps only for common looping idioms?

Anyway, the workaround is simple enough...
    test
        |x|
        x := nil. "silence critiques"
        [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: Critiques - Temporaries read before written.

CyrilFerlicot

On dim. 15 avr. 2018 at 16:43, Ben Coman <[hidden email]> wrote:
The greater prominence of Critiques in Calypso 
encourages me to try to clean them out.

I bumped into a false positive "Temporaries read before written."
that I've condensed to the following example.

    test
        |x|
        [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]

Now before I log an Issue, is it feasible to be able to recognise this?
Perhaps only for common looping idioms?

Anyway, the workaround is simple enough...

Hi Ben,

I do not have an answer regarding the variables written in the blocks, but for false positive you can also just exclude a rule for the scope of the method, class, package and image IIRC. There was a presentation at esug about it. 
 
I'm on phone and it's hard to search for the link currently. 

    test
        |x|
        x := nil. "silence critiques"
        [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]

cheers -ben
--
Cyril Ferlicot
https://ferlicot.fr
Reply | Threaded
Open this post in threaded view
|

Re: Critiques - Temporaries read before written.

Martin McClure-2
In reply to this post by Ben Coman
On 04/15/2018 07:42 AM, Ben Coman wrote:
The greater prominence of Critiques in Calypso 
encourages me to try to clean them out.

I bumped into a false positive "Temporaries read before written."
that I've condensed to the following example.

    test
        |x|
        [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]

Now before I log an Issue, is it feasible to be able to recognise this?
Perhaps only for common looping idioms?

In this example, the first runtime reference to x is to send it #ifNil:. So technically, x *is* being read before being written, at least if you count sending it a message as "reading" (which seems a reasonable interpretation to me).

Anyway, the workaround is simple enough...
    test
        |x|
        x := nil. "silence critiques"
        [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]
Probably not a terrible idea to be explicit about initializing to nil, thereby revealing the developer's intent that this variable be nil rather than relying on the default initialization to nil.

So I'd lean towards leaving the critique as-is. If a developer knows what they did was safe, they can either ignore the critique, exclude the critique, or put in the explicit initialization to nil. I think I prefer explicit initialization.

Regards,
-Martin
Reply | Threaded
Open this post in threaded view
|

Re: Critiques - Temporaries read before written.

Denis Kudriashov


2018-04-15 20:40 GMT+02:00 Martin McClure <[hidden email]>:
On 04/15/2018 07:42 AM, Ben Coman wrote:
The greater prominence of Critiques in Calypso 
encourages me to try to clean them out.

I bumped into a false positive "Temporaries read before written."
that I've condensed to the following example.

    test
        |x|
        [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]

Now before I log an Issue, is it feasible to be able to recognise this?
Perhaps only for common looping idioms?

In this example, the first runtime reference to x is to send it #ifNil:. So technically, x *is* being read before being written, at least if you count sending it a message as "reading" (which seems a reasonable interpretation to me).

Maybe critique can be a bit smarter and check that sent message is known by nil.
 


Anyway, the workaround is simple enough...
    test
        |x|
        x := nil. "silence critiques"
        [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]
Probably not a terrible idea to be explicit about initializing to nil, thereby revealing the developer's intent that this variable be nil rather than relying on the default initialization to nil.

So I'd lean towards leaving the critique as-is. If a developer knows what they did was safe, they can either ignore the critique, exclude the critique, or put in the explicit initialization to nil. I think I prefer explicit initialization.

Regards,
-Martin

Reply | Threaded
Open this post in threaded view
|

Re: Critiques - Temporaries read before written.

John Brant-2
In reply to this post by Ben Coman

> On Apr 15, 2018, at 9:42 AM, Ben Coman <[hidden email]> wrote:
>
> The greater prominence of Critiques in Calypso
> encourages me to try to clean them out.
>
> I bumped into a false positive "Temporaries read before written."
> that I've condensed to the following example.
>
>     test
>         |x|
>         [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]
>
> Now before I log an Issue, is it feasible to be able to recognise this?

In general one would need to look at the implementor to know which blocks are always evaluated and in what order. For example, someone could have defined whileNil: as:

        whileNil: aBlock
                aBlock value

In this case, it isn’t a false positive since the "x := nil” is never executed and the first use of “x” is the “x ifNil: …”.

> Perhaps only for common looping idioms?

It does look for common looping idioms. It treats the four whileTrue/False messages different than normal messages. It also treats ifTrue:ifFalse:/ifFalse:ifTrue: messages differently. #whileNil: isn’t what I call a common looping idiom as it seems to have been added in 2007 (about 13 years after the read before written tester was written), and has 0 senders in my image.

I think a better change would be adding knowledge about and:/or: combined with ifTrue:, whileTrue:, etc. For example,

        (self someCondition and: [(x := self value) > 0]) ifTrue: [Transcript show: x printString]

In this case, x is reported as potentially read before written, but it can’t be.


John Brant
Reply | Threaded
Open this post in threaded view
|

Re: Critiques - Temporaries read before written.

Eliot Miranda-2
In reply to this post by Martin McClure-2
Hi Martin,

On Apr 15, 2018, at 11:40 AM, Martin McClure <[hidden email]> wrote:

On 04/15/2018 07:42 AM, Ben Coman wrote:
The greater prominence of Critiques in Calypso 
encourages me to try to clean them out.

I bumped into a false positive "Temporaries read before written."
that I've condensed to the following example.

    test
        |x|
        [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]

Now before I log an Issue, is it feasible to be able to recognise this?
Perhaps only for common looping idioms?

In this example, the first runtime reference to x is to send it #ifNil:. So technically, x *is* being read before being written, at least if you count sending it a message as "reading" (which seems a reasonable interpretation to me).

How so?  The first run-time reference to xcould be in either of the two non-unlined blocks.  The assignment to x in [ x := nil ] could precede the read if (as of course it does, but we can't guarantee) BlockClosure>>whileNil: chooses to evaluate it before [ x ifNil: [ x := 1] ].   The issue is that the code is ambiguous; it depends on the implementation of whileNil: and hence should be reported as "may" rather than "shall".


Anyway, the workaround is simple enough...
    test
        |x|
        x := nil. "silence critiques"
        [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]
Probably not a terrible idea to be explicit about initializing to nil, thereby revealing the developer's intent that this variable be nil rather than relying on the default initialization to nil.

I've never agreed with this.  It is in the language spec that all pointer variables, including temporaries, are initialized with nil, and repeating an initialization shows ignorance, and makes me suspect the rest of the code.  Smalltalk intentionally ensures that all variables are initialised and for good reason.  It's similar, but worse, than people adding an explicit ifFalse: [nil] to an ifTrue: when the default return value for a false ifTrue: is indeed nil. We should strive for literacy, not bastardise things for the ignorant since repeating a mistake is a route to establishing the mistake as common parlance hence losing elegance and concision.

So I'd lean towards leaving the critique as-is. If a developer knows what they did was safe, they can either ignore the critique, exclude the critique, or put in the explicit initialization to nil. I think I prefer explicit initialization.

The critique is wrong.  It can only say "may be read before written" and a human being in the same situation would soon observe that the receiver block is always evaluated before the argument block and so not earn at all.  Benis right; this is a false positive.  However, saying "may" is at least in the spirit of the language given the possibility of reimplementing whileNil:.


Regards,
-Martin
Reply | Threaded
Open this post in threaded view
|

Re: Critiques - Temporaries read before written.

Ben Coman
In reply to this post by Denis Kudriashov
On 16 April 2018 at 03:07, Denis Kudriashov <[hidden email]> wrote:

>
>
>
> 2018-04-15 20:40 GMT+02:00 Martin McClure <[hidden email]>:
>>
>> On 04/15/2018 07:42 AM, Ben Coman wrote:
>>
>> The greater prominence of Critiques in Calypso
>> encourages me to try to clean them out.
>>
>> I bumped into a false positive "Temporaries read before written."
>> that I've condensed to the following example.
>>
>>     test
>>         |x|
>>         [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]
>>
>> Now before I log an Issue, is it feasible to be able to recognise this?
>> Perhaps only for common looping idioms?
>>
>>
>> In this example, the first runtime reference to x is to send it #ifNil:. So technically, x *is* being read before being written, at least if you count sending it a message as "reading" (which seems a reasonable interpretation to me).
>
>
> Maybe critique can be a bit smarter and check that sent message is known by nil.

That sounds like a nice approach.  Browsing the messages of Undefined,
maybe its worthwhile to restrict this to messages containing the the
string "nil", because this shows explicit intent and would cover the
most common//useful cases.
https://pharo.manuscript.com/f/cases/21704/Critiques-Temporaries-read-before-written-versus-nil-checking-messages

cheers -ben

Reply | Threaded
Open this post in threaded view
|

Re: Critiques - Temporaries read before written.

John Brant-2
On 04/15/2018 05:02 PM, Ben Coman wrote:

> On 16 April 2018 at 03:07, Denis Kudriashov <[hidden email]> wrote:
>>
>>
>>
>> 2018-04-15 20:40 GMT+02:00 Martin McClure <[hidden email]>:
>>>
>>> On 04/15/2018 07:42 AM, Ben Coman wrote:
>>>
>>> The greater prominence of Critiques in Calypso
>>> encourages me to try to clean them out.
>>>
>>> I bumped into a false positive "Temporaries read before written."
>>> that I've condensed to the following example.
>>>
>>>     test
>>>         |x|
>>>         [ x := nil ] whileNil: [ x ifNil: [ x := 1] ]
>>>
>>> Now before I log an Issue, is it feasible to be able to recognise this?
>>> Perhaps only for common looping idioms?
>>>
>>>
>>> In this example, the first runtime reference to x is to send it #ifNil:. So technically, x *is* being read before being written, at least if you count sending it a message as "reading" (which seems a reasonable interpretation to me).
>>
>>
>> Maybe critique can be a bit smarter and check that sent message is known by nil.
>
> That sounds like a nice approach.  Browsing the messages of Undefined,
> maybe its worthwhile to restrict this to messages containing the the
> string "nil", because this shows explicit intent and would cover the
> most common//useful cases.
> https://pharo.manuscript.com/f/cases/21704/Critiques-Temporaries-read-before-written-versus-nil-checking-messages

You may break extract method if you change this. I haven't tried, but I
think something like this would break -- extract the last line of this
method:

method
        | x |
        x := self someValue.
        x isNil ifTrue: [Transcript show: 'nil']

You should get something like this:

method
        | x |
        x := self someValue.
        self extractedMethod: x

extractedMethod: x
        x isNil ifTrue: [Transcript show: 'nil']

However, if you change what is considered read before written, then you
would get something like this:

method
        | x |
        x := self someValue.
        self extractedMethod

extractedMethod
        | x |
        x isNil ifTrue: [Transcript show: 'nil']


John Brant