From 437ac874a052619a554b3df94ed8b1d4eb0a1ee9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 3 Oct 2016 16:24:47 +0200 Subject: APIDump: Check param and return types. --- Server/Plugins/APIDump/APIDesc.lua | 66 +++++--- Server/Plugins/APIDump/Classes/BlockEntities.lua | 4 +- Server/Plugins/APIDump/Classes/WebAdmin.lua | 4 +- Server/Plugins/APIDump/main_APIDump.lua | 189 ++++++++++++++++++----- 4 files changed, 204 insertions(+), 59 deletions(-) diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index 1e8454b37..7ec978d3b 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -3706,7 +3706,12 @@ end }, Clear = { - Returns = "self", + Returns = + { + { + Type = "self", + }, + }, Notes = "Removes all parts from this object", }, constructor = @@ -6080,8 +6085,16 @@ local Hash = cCryptoHash.sha1HexString("DataToHash") }, Notes = "Returns true if the potion with the given damage is drinkable", }, - }, - }, + }, -- Functions + ConstantGroups = + { + eType = + { + Include = { "eff.*" }, + }, + }, -- ConstantGroups + }, -- cEntityEffect + cFile = { Desc = [[ @@ -10248,7 +10261,7 @@ a_Player:OpenWindow(Window); { { Name = "MobType", - Type = "Globals#MobType", + Type = "Globals#eMonsterType", }, }, Returns = @@ -11075,7 +11088,7 @@ a_Player:OpenWindow(Window); { { Name = "GameMode", - Type = "Globals#GameMode", + Type = "Globals#eGameMode", }, }, Notes = "(OBSOLETE) Returns the current resolved game mode of the player. If the player is set to inherit the world's gamemode, returns that instead. See also GetGameMode() and IsGameModeXXX() functions. Note that this function is the same as GetGameMode(), use that function instead.", @@ -11175,7 +11188,7 @@ a_Player:OpenWindow(Window); { { Name = "GameMode", - Type = "Globals#GameMode", + Type = "Globals#eGameMode", }, }, Notes = "Returns the player's gamemode. The player may have their gamemode unassigned, in which case they inherit the gamemode from the current {{cWorld|world}}.
NOTE: Instead of comparing the value returned by this function to the gmXXX constants, use the IsGameModeXXX() functions. These functions handle the gamemode inheritance automatically.", @@ -11763,7 +11776,7 @@ a_Player:OpenWindow(Window); }, { Name = "World", - Type = "cWorld*", + Type = "cWorld", IsOptional = true, }, }, @@ -11896,7 +11909,7 @@ a_Player:OpenWindow(Window); { { Name = "NewGameMode", - Type = "Globals#GameMode", + Type = "Globals#eGameMode", }, }, Notes = "Sets the gamemode for the player. The new gamemode overrides the world's default gamemode, unless it is set to gmInherit.", @@ -14242,8 +14255,17 @@ end { Notes = "A workbench (crafting table) window", }, - }, - }, + }, -- Constants + + ConstantGroups = + { + WindowType = + { + Include = { "wt.*" }, + } + }, -- ConstantGroups + }, -- cWindow + cWorld = { Desc = [[ @@ -15865,10 +15887,10 @@ function OnAllChunksAvailable() All return values from the callbacks are i Returns = { { - Type = "cScoreBoard", + Type = "cScoreboard", }, }, - Notes = "Returns the {{cScoreBoard|ScoreBoard}} object used by this world. ", + Notes = "Returns the {{cScoreboard|Scoreboard}} object used by this world. ", }, GetSeed = { @@ -18235,7 +18257,7 @@ World:ForEachEntity( { { Name = "BiomeType", - Type = "Globals#BiomeTypes", + Type = "Globals#EMCSBiome", }, }, Notes = "Converts a string representation to a {{Globals#BiomeTypes|BiomeType}} enumerated value. Returns biInvalidBiome if the input is not a recognized biome.", @@ -18253,7 +18275,7 @@ World:ForEachEntity( { { Name = "DamageType", - Type = "Globals#DamageType", + Type = "Globals#eDamageType", }, }, Notes = "Converts a string representation to a {{Globals#DamageType|DamageType}} enumerated value. Returns -1 if the inupt is not a recognized damage type.", @@ -18271,10 +18293,10 @@ World:ForEachEntity( { { Name = "Dimension", - Type = "Globals#WorldDimension", + Type = "Globals#eDimension", }, }, - Notes = "Converts a string representation to a {{Globals#WorldDimension|Dimension}} enumerated value. Returns dimNotSet if the input is not a recognized dimension.", + Notes = "Converts a string representation to a {{Globals#eDimension|eDimension}} enumerated value. Returns dimNotSet if the input is not a recognized dimension.", }, StringToItem = { @@ -18310,10 +18332,10 @@ World:ForEachEntity( { { Name = "MobType", - Type = "Globals#MobType", + Type = "eMonsterType", }, }, - Notes = "(DEPRECATED!) Please use cMonster:StringToMobType(). Converts a string representation to a {{Globals#MobType|MobType}} enumerated value", + Notes = "(DEPRECATED!) Please use cMonster:StringToMobType(). Converts a string representation to an {{Globals#eMonsterType|eMonsterType}} enumerated value", }, StripColorCodes = { @@ -18688,6 +18710,14 @@ World:ForEachEntity( on the message's type. ]], }, + eMobHeadType = + { + Include = "SKULL_TYPE_.*", + }, + eMobHeadRotation = + { + Include = "SKULL_ROTATION_.*", + }, eMonsterType = { Include = diff --git a/Server/Plugins/APIDump/Classes/BlockEntities.lua b/Server/Plugins/APIDump/Classes/BlockEntities.lua index 3f3552c99..7616d7180 100644 --- a/Server/Plugins/APIDump/Classes/BlockEntities.lua +++ b/Server/Plugins/APIDump/Classes/BlockEntities.lua @@ -1254,7 +1254,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(), { { Name = "MobType", - Type = "Globals#MobType", + Type = "eMonsterType", }, }, Notes = "Returns the entity type that will be spawn by this mob spawner.", @@ -1306,7 +1306,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(), { { Name = "MobType", - Type = "Globals#MobType", + Type = "eMonsterType", }, }, Notes = "Sets the type of the mob that will be spawned by this mob spawner.", diff --git a/Server/Plugins/APIDump/Classes/WebAdmin.lua b/Server/Plugins/APIDump/Classes/WebAdmin.lua index faa81dc82..583278e5f 100644 --- a/Server/Plugins/APIDump/Classes/WebAdmin.lua +++ b/Server/Plugins/APIDump/Classes/WebAdmin.lua @@ -96,8 +96,8 @@ return Params = { { - Name = "HTTPRequest", - Type = "Request", + Name = "Request", + Type = "HTTPRequest", }, }, Returns = diff --git a/Server/Plugins/APIDump/main_APIDump.lua b/Server/Plugins/APIDump/main_APIDump.lua index 6dec8e159..c2d70734d 100644 --- a/Server/Plugins/APIDump/main_APIDump.lua +++ b/Server/Plugins/APIDump/main_APIDump.lua @@ -1816,43 +1816,12 @@ end -local function HandleWebAdminDump(a_Request) - if (a_Request.PostParams["Dump"] ~= nil) then - DumpApi() - end - return - [[ -

Pressing the button will generate the API dump on the server. Note that this can take some time.

-
- ]] -end - - - - - -local function HandleCmdApi(a_Split) - DumpApi() - return true -end - - - - - -local function HandleCmdApiShow(a_Split, a_EntireCmd) - os.execute("API" .. cFile:GetPathSeparator() .. "index.html") - return true, "Launching the browser to show the API docs..." -end - - - - - -local function HandleCmdApiCheck(a_Split, a_EntireCmd) +--- Checks the currently undocumented symbols against an "official" undocumented symbol list +-- Returns an array-table of strings representing the newly-undocumented symbol names +local function CheckNewUndocumentedSymbols() -- Download the official API stats on undocumented stuff: -- (We need a blocking downloader, which is impossible with the current cNetwork API) - assert(os.execute("wget -O official_undocumented.lua http://apidocs.cuberite.org/_undocumented.lua")) + assert(os.execute("wget -q -O official_undocumented.lua http://apidocs.cuberite.org/_undocumented.lua")) local OfficialStats = cFile:ReadWholeFile("official_undocumented.lua") if (OfficialStats == "") then return true, "Cannot load official stats" @@ -1916,7 +1885,7 @@ local function HandleCmdApiCheck(a_Split, a_EntireCmd) -- Bail out if no items found: if not(res[1]) then - return true, "No new undocumented functions" + return end -- Save any found items to a file: @@ -1925,7 +1894,153 @@ local function HandleCmdApiCheck(a_Split, a_EntireCmd) f:write("\n") f:close() - return true, "Newly undocumented items: " .. #res .. "\n" .. table.concat(res, "\n") + return res +end + + + + + +--- Checks the API description for unknown types listed in Params or Returns +-- Returns an array-table of { Location = "cClass:function(), param #1", Type = "UnknownType" } +-- Returns nil if no unknown types are found +local function CheckBadTypes() + -- Load the API and preprocess known types: + local api = PrepareApi() + local knownTypes = + { + string = true, + number = true, + boolean = true, + any = true, + self = true, + table = true, + ["function"] = true, + ["..."] = true, + ["SQLite DB object"] = true, + [""] = true, -- Allow "" types, for now, until the API is properly documented + } + for _, clsDesc in ipairs(api) do + knownTypes[clsDesc.Name] = true -- The class is a known type + for grpName, _ in pairs(clsDesc.ConstantGroups or {}) do -- All class' enums are known types (with namespacing) + knownTypes[clsDesc.Name .. "#" .. grpName] = true + end + if (clsDesc.Name == "Globals") then + for grpName, _ in pairs(clsDesc.ConstantGroups or {}) do -- All Globals' enums are known types without namespacing, too + knownTypes[grpName] = true + end + end + end -- for cls - classes + + -- Check types: + local res = {} + for _, clsDesc in ipairs(api) do + for _, fnDesc in ipairs(clsDesc.Functions or {}) do + local fnName = fnDesc.Name + local fn = fnDesc[1] and fnDesc or { fnDesc } -- Unify the format, fn is an array of function signatures + for idxS, signature in ipairs(fn) do + for idxP, param in ipairs(signature.Params or {}) do + if not(knownTypes[param.Type]) then + table.insert(res, { + Location = string.format("%s:%s(), signature #%d, param #%d", clsDesc.Name, fnName, idxS, idxP), + Type = param.Type, + }) + end + end -- for param + if (type(signature.Returns) == "table") then + for idxR, ret in ipairs(signature.Returns) do + if not(knownTypes[ret.Type]) then + table.insert(res, { + Location = string.format("%s:%s(), signature #%d, return #%d", clsDesc.Name, fnName, idxS, idxR), + Type = ret.Type, + }) + end + end -- for ret + elseif not(signature.Returns) then + else + table.insert(res, { + Location = string.format("%s:%s(), signature #%d, return string", clsDesc.Name, fnName, idxS), + Type = tostring(signature.Returns), + }) + end + end -- for signature + end -- for fn - functions + end -- for cls - classes + + -- If no problems found, bail out: + if not(res[1]) then + return + end + + -- Write the problems into a file: + local f = io.open("UnknownTypes.lua", "w") + f:write("return\n{\n") + for _, item in ipairs(res) do + f:write("\t{ ", string.format("{ Location = %q, Type = %q", item.Location, item.Type), "},\n") + end + f:write("}\n") + f:close() + return res +end + + + + + +local function HandleWebAdminDump(a_Request) + if (a_Request.PostParams["Dump"] ~= nil) then + DumpApi() + end + return + [[ +

Pressing the button will generate the API dump on the server. Note that this can take some time.

+
+ ]] +end + + + + + +local function HandleCmdApi(a_Split) + DumpApi() + return true +end + + + + + +local function HandleCmdApiShow(a_Split, a_EntireCmd) + os.execute("API" .. cFile:GetPathSeparator() .. "index.html") + return true, "Launching the browser to show the API docs..." +end + + + + + +local function HandleCmdApiCheck(a_Split, a_EntireCmd) + -- Check the Params and Returns types: + LOG("Checking API for bad types...") + local badTypes = CheckBadTypes() + if (badTypes) then + -- Serialize into descriptions: + local descs = {} + for idx, t in ipairs(badTypes) do + descs[idx] = string.format("Location %q, type %q", t.Location, t.Type) + end + return true, "Found bad types:\n" .. table.concat(descs, "\n") + end + + -- Check for new symbols that are not documented: + LOG("Checking API for newly undocumented symbols...") + local newUndocumented = CheckNewUndocumentedSymbols() + if (newUndocumented) then + return true, "Found new undocumented symbols:\n" .. table.concat(newUndocumented, "\n") + end + + return true, "API check completed successfully" end -- cgit v1.2.3