Strict/Nonstrict mode exposes local variable types to function scopes that should not be able to see them

Issue Type: Other
Impact: Moderate
Frequency: Constantly
Date First Experienced: 2021-04-10 00:04:00 (-06:00)
Date Last Experienced: 2021-04-10 00:04:00 (-06:00)

Reproduction Steps:
Reproducing this is fairly simple. Simply compare the following snippets:

--!nocheck

local function usesFoo()
	local x: number = foo
end

local foo = 'fighters'
--!strict

local function usesFoo()
	local x: number = foo
end

local foo = 'fighters'

Both of these will emit a warning; however, the warning in the strict mode example is not the correct warning.
Now consider the two following snippets:

--!nocheck

local function usesFoo()
	local x: string = foo
end

local foo = 'fighters'
--!strict

local function usesFoo()
	local x: string = foo
end

local foo = 'fighters'

In this case, the --!nocheck mode snippet will correctly identify the global variable as missing; however, in --!strict mode, there is no warning.

This matters because I ran into a bug that affected my code at runtime but was not caught in static analysis.

Expected Behavior:
I expect the following snippets to only emit an “unknown global” warning.

--!strict

local function usesFoo()
	local x: string = foo
end

local foo = 'fighters'
--!strict

local function usesFoo()
	local x: number = foo
end

local foo = 'fighters'

Actual Behavior:
In the first example (–!nocheck mode), the variable is correctly identified as an “unknown global” and a warning shows up
image
However, in strict or nonstrict mode, this variable’s type will be exposed even if the variable is out of scope with the function referencing it.


If I type x as “string” instead of “number”, the warning disappears entirely:
image

Workaround:
You could, hypothetically, set your code to --!nocheck mode to catch unknown globals, then switch it back to --!strict mode for everything else I guess?

5 Likes

I can reproduce this if the immediate steps above are followed:

image


HOWEVER, this behavior is inconsistent. See the below examples:

image

image

image

image

image

image

image


After some tests, I can say the reproduction steps are binded some more specific rules:

  • This does not occur in private scopes that are not userdata-functions.
  • The function can be either local or global.
  • The Foo variable in this case only caused the specific problem if it was defined in the immediately above environment.
  • Does not reproduce for embedded functions, such as that with setmetatable.

Hope this information is useful.

2 Likes

Thanks for the report! We’ve filed a ticket to our internal database and we’ll follow up when we have an update for you.

5 Likes