|
| 1 | +local files = require 'files' |
| 2 | +local guide = require 'parser.guide' |
| 3 | +local vm = require 'vm' |
| 4 | +local lang = require 'language' |
| 5 | +local await = require 'await' |
| 6 | + |
| 7 | +---@param node vm.node |
| 8 | +---@return string|nil |
| 9 | +local function getTruthiness(node) |
| 10 | + if node:alwaysTruthy() then |
| 11 | + return 'true' |
| 12 | + end |
| 13 | + if node:alwaysFalsy() then |
| 14 | + return 'false' |
| 15 | + end |
| 16 | + return nil |
| 17 | +end |
| 18 | + |
| 19 | +---@param infer vm.infer |
| 20 | +---@param uri uri |
| 21 | +---@return boolean|nil |
| 22 | +local function isBooleanOnly(infer, uri) |
| 23 | + if infer:hasAny(uri) or infer:hasUnknown(uri) then |
| 24 | + return nil |
| 25 | + end |
| 26 | + local hasBoolean = infer:hasType(uri, 'boolean') |
| 27 | + if not hasBoolean then |
| 28 | + return false |
| 29 | + end |
| 30 | + for view in infer:eachView(uri) do |
| 31 | + if view ~= 'boolean' then |
| 32 | + return false |
| 33 | + end |
| 34 | + end |
| 35 | + return true |
| 36 | +end |
| 37 | + |
| 38 | +---@param source parser.object? |
| 39 | +---@param uri uri |
| 40 | +---@param callback fun(result: diag.result) |
| 41 | +local function checkExpression(source, uri, callback) |
| 42 | + if not source then |
| 43 | + return |
| 44 | + end |
| 45 | + local node = vm.compileNode(source) |
| 46 | + local truthiness = getTruthiness(node) |
| 47 | + if truthiness then |
| 48 | + callback { |
| 49 | + start = source.start, |
| 50 | + finish = source.finish, |
| 51 | + message = lang.script('DIAG_BOOLEAN_CONTEXT_ALWAYS', truthiness), |
| 52 | + } |
| 53 | + return |
| 54 | + end |
| 55 | + local infer = vm.getInfer(source) |
| 56 | + local onlyBoolean = isBooleanOnly(infer, uri) |
| 57 | + if onlyBoolean == true or onlyBoolean == nil then |
| 58 | + return |
| 59 | + end |
| 60 | + callback { |
| 61 | + start = source.start, |
| 62 | + finish = source.finish, |
| 63 | + message = lang.script('DIAG_BOOLEAN_CONTEXT_NONBOOLEAN', infer:view(uri)), |
| 64 | + } |
| 65 | +end |
| 66 | + |
| 67 | +---@async |
| 68 | +return function (uri, callback) |
| 69 | + local state = files.getState(uri) |
| 70 | + if not state then |
| 71 | + return |
| 72 | + end |
| 73 | + |
| 74 | + ---@async |
| 75 | + guide.eachSourceTypes(state.ast, {'ifblock', 'elseifblock', 'while', 'repeat'}, function (source) |
| 76 | + await.delay() |
| 77 | + if source.filter |
| 78 | + and source.filter.type == 'binary' |
| 79 | + and source.filter.op |
| 80 | + and (source.filter.op.type == 'and' or source.filter.op.type == 'or') then |
| 81 | + return |
| 82 | + end |
| 83 | + checkExpression(source.filter, uri, callback) |
| 84 | + end) |
| 85 | + |
| 86 | + ---@async |
| 87 | + guide.eachSourceType(state.ast, 'binary', function (source) |
| 88 | + await.delay() |
| 89 | + local op = source.op and source.op.type |
| 90 | + if op ~= 'and' and op ~= 'or' then |
| 91 | + return |
| 92 | + end |
| 93 | + checkExpression(source[1], uri, callback) |
| 94 | + end) |
| 95 | +end |
0 commit comments