mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	fix(lsp): better handling of "*" configs
Problem:
If a config name contains "*" it causes rtp discovery of `lsp/` to
consider the `*` as a wildcard and could lead to strange and unintended
behaviour. For example, accessing the `'*'` config from a `lsp/` file
would cause an infinite loop.
Solution:
- Explicitly disallow a config name from containing wildcards, with the
  exception of `'*'`.
- When Resolving `'*'` config, skip the rtp step.
(cherry picked from commit 2ee896201c)
			
			
This commit is contained in:
		
				
					committed by
					
						
						Lewis Russell
					
				
			
			
				
	
			
			
			
						parent
						
							a4b6705e87
						
					
				
				
					commit
					1e8e74dbff
				
			@@ -362,6 +362,19 @@ local function invalidate_enabled_config(name)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- @param name any
 | 
			
		||||
local function validate_config_name(name)
 | 
			
		||||
  validate('name', name, function(value)
 | 
			
		||||
    if type(value) ~= 'string' then
 | 
			
		||||
      return false
 | 
			
		||||
    end
 | 
			
		||||
    if value ~= '*' and value:match('%*') then
 | 
			
		||||
      return false, 'LSP config name cannot contain wildcard ("*")'
 | 
			
		||||
    end
 | 
			
		||||
    return true
 | 
			
		||||
  end, 'non-wildcard string')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- @nodoc
 | 
			
		||||
--- @class vim.lsp.config
 | 
			
		||||
--- @field [string] vim.lsp.Config
 | 
			
		||||
@@ -371,11 +384,16 @@ lsp.config = setmetatable({ _configs = {} }, {
 | 
			
		||||
  --- @param name string
 | 
			
		||||
  --- @return vim.lsp.Config
 | 
			
		||||
  __index = function(self, name)
 | 
			
		||||
    validate('name', name, 'string')
 | 
			
		||||
    validate_config_name(name)
 | 
			
		||||
 | 
			
		||||
    local rconfig = lsp._enabled_configs[name] or {}
 | 
			
		||||
 | 
			
		||||
    if not rconfig.resolved_config then
 | 
			
		||||
      if name == '*' then
 | 
			
		||||
        rconfig.resolved_config = lsp.config._configs['*'] or {}
 | 
			
		||||
        return rconfig.resolved_config
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      -- Resolve configs from lsp/*.lua
 | 
			
		||||
      -- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites.
 | 
			
		||||
      local rtp_config --- @type vim.lsp.Config?
 | 
			
		||||
@@ -385,12 +403,12 @@ lsp.config = setmetatable({ _configs = {} }, {
 | 
			
		||||
          --- @type vim.lsp.Config?
 | 
			
		||||
          rtp_config = vim.tbl_deep_extend('force', rtp_config or {}, config)
 | 
			
		||||
        else
 | 
			
		||||
          log.warn(string.format('%s does not return a table, ignoring', v))
 | 
			
		||||
          log.warn(('%s does not return a table, ignoring'):format(v))
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      if not rtp_config and not self._configs[name] then
 | 
			
		||||
        log.warn(string.format('%s does not have a configuration', name))
 | 
			
		||||
        log.warn(('%s does not have a configuration'):format(name))
 | 
			
		||||
        return
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
@@ -410,7 +428,7 @@ lsp.config = setmetatable({ _configs = {} }, {
 | 
			
		||||
  --- @param name string
 | 
			
		||||
  --- @param cfg vim.lsp.Config
 | 
			
		||||
  __newindex = function(self, name, cfg)
 | 
			
		||||
    validate('name', name, 'string')
 | 
			
		||||
    validate_config_name(name)
 | 
			
		||||
    validate('cfg', cfg, 'table')
 | 
			
		||||
    invalidate_enabled_config(name)
 | 
			
		||||
    self._configs[name] = cfg
 | 
			
		||||
@@ -420,7 +438,7 @@ lsp.config = setmetatable({ _configs = {} }, {
 | 
			
		||||
  --- @param name string
 | 
			
		||||
  --- @param cfg vim.lsp.Config
 | 
			
		||||
  __call = function(self, name, cfg)
 | 
			
		||||
    validate('name', name, 'string')
 | 
			
		||||
    validate_config_name(name)
 | 
			
		||||
    validate('cfg', cfg, 'table')
 | 
			
		||||
    invalidate_enabled_config(name)
 | 
			
		||||
    self[name] = vim.tbl_deep_extend('force', self._configs[name] or {}, cfg)
 | 
			
		||||
 
 | 
			
		||||
@@ -6414,5 +6414,36 @@ describe('LSP', function()
 | 
			
		||||
        filetypes = true,
 | 
			
		||||
      }, 'cannot start foo due to config error: .* filetypes: expected table, got boolean')
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('does not allow wildcards in config name', function()
 | 
			
		||||
      local err =
 | 
			
		||||
        '.../lsp.lua:0: name: expected non%-wildcard string, got foo%*%. Info: LSP config name cannot contain wildcard %("%*"%)'
 | 
			
		||||
 | 
			
		||||
      matches(
 | 
			
		||||
        err,
 | 
			
		||||
        pcall_err(exec_lua, function()
 | 
			
		||||
          local _ = vim.lsp.config['foo*']
 | 
			
		||||
        end)
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      matches(
 | 
			
		||||
        err,
 | 
			
		||||
        pcall_err(exec_lua, function()
 | 
			
		||||
          vim.lsp.config['foo*'] = {}
 | 
			
		||||
        end)
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      matches(
 | 
			
		||||
        err,
 | 
			
		||||
        pcall_err(exec_lua, function()
 | 
			
		||||
          vim.lsp.config('foo*', {})
 | 
			
		||||
        end)
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      -- Exception for '*'
 | 
			
		||||
      pcall(exec_lua, function()
 | 
			
		||||
        vim.lsp.config('*', {})
 | 
			
		||||
      end)
 | 
			
		||||
    end)
 | 
			
		||||
  end)
 | 
			
		||||
end)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user