diff --git a/dub.json b/dub.json index cf61ec7..021543e 100644 --- a/dub.json +++ b/dub.json @@ -14,7 +14,7 @@ "dependencies" : { "libdparse": "~>0.8.0-alpha.5", "dsymbol" : "~>0.3.0-alpha.3", - "inifiled" : ">=1.0.2", + "inifiled" : "~>1.1.0", "emsi_containers" : "~>0.6.0", "libddoc" : "~>0.3.0-beta.1", "stdx-allocator" : "~>2.77.0" diff --git a/src/dscanner/analysis/properly_documented_public_functions.d b/src/dscanner/analysis/properly_documented_public_functions.d index 7772184..68e9793 100644 --- a/src/dscanner/analysis/properly_documented_public_functions.d +++ b/src/dscanner/analysis/properly_documented_public_functions.d @@ -119,8 +119,8 @@ class ProperlyDocumentedPublicFunctions : BaseAnalyzer return; auto comment = setLastDdocParams(decl.name.line, decl.name.column, decl.comment); - checkDdocParams(decl.name.line, decl.name.column, decl.parameters); - checkDdocParams(decl.name.line, decl.name.column, decl.templateParameters); + + checkDdocParams(decl.name.line, decl.name.column, decl.parameters, decl.templateParameters); enum voidType = tok!"void"; @@ -187,36 +187,76 @@ private: return comment; } - void checkDdocParams(size_t line, size_t column, const Parameters params) + void checkDdocParams(size_t line, size_t column, const Parameters params, + const TemplateParameters templateParameters = null) { - import std.algorithm.searching : canFind; + import std.array : array; + import std.algorithm.searching : canFind, countUntil; + import std.algorithm.iteration : map; + import std.algorithm.mutation : remove; + import std.range : indexed, iota; + + // convert templateParameters into a string[] for faster access + const(TemplateParameter)[] templateList; + if (const tp = templateParameters) + if (const tpl = tp.templateParameterList) + templateList = tpl.items; + string[] tlList = templateList.map!(a => templateParamName(a)).array; + + // make a copy of all parameters and remove the seen ones later during the loop + size_t[] unseenTemplates = templateList.length.iota.array; if (lastSeenFun.active && params !is null) foreach (p; params.parameters) { + string templateName; + if (const t = p.type) + if (const t2 = t.type2) + if (const tip = t2.typeIdentifierPart) + if (const iot = tip.identifierOrTemplateInstance) + templateName = iot.identifier.text; + + const idx = tlList.countUntil(templateName); + if (idx >= 0) + { + unseenTemplates = unseenTemplates.remove(idx); + tlList = tlList.remove(idx); + // documenting template parameter should be allowed + lastSeenFun.params[templateName] = true; + } + if (!lastSeenFun.ddocParams.canFind(p.name.text)) addErrorMessage(line, column, MISSING_PARAMS_KEY, format(MISSING_PARAMS_MESSAGE, p.name.text)); else lastSeenFun.params[p.name.text] = true; } + + // now check the remaining, not used template parameters + auto unseenTemplatesArr = templateList.indexed(unseenTemplates).array; + checkDdocParams(line, column, unseenTemplatesArr); } void checkDdocParams(size_t line, size_t column, const TemplateParameters templateParams) { - import std.algorithm.searching : canFind; if (lastSeenFun.active && templateParams !is null && templateParams.templateParameterList !is null) - foreach (p; templateParams.templateParameterList.items) - { - auto name = templateParamName(p); - assert(name, "Invalid template parameter name."); // this shouldn't happen - if (!lastSeenFun.ddocParams.canFind(name)) - addErrorMessage(line, column, MISSING_PARAMS_KEY, - format(MISSING_TEMPLATE_PARAMS_MESSAGE, name)); - else - lastSeenFun.params[name] = true; - } + checkDdocParams(line, column, templateParams.templateParameterList.items); + } + + void checkDdocParams(size_t line, size_t column, const TemplateParameter[] templateParams) + { + import std.algorithm.searching : canFind; + foreach (p; templateParams) + { + const name = templateParamName(p); + assert(name, "Invalid template parameter name."); // this shouldn't happen + if (!lastSeenFun.ddocParams.canFind(name)) + addErrorMessage(line, column, MISSING_PARAMS_KEY, + format(MISSING_TEMPLATE_PARAMS_MESSAGE, name)); + else + lastSeenFun.params[name] = true; + } } static string templateParamName(const TemplateParameter p) @@ -694,7 +734,6 @@ string bar(string val){} template bar(string val){} }c, sac); - stderr.writeln("Unittest for ProperlyDocumentedPublicFunctions passed."); } unittest @@ -725,3 +764,38 @@ template abcde(Args ...) { } }c, sac); } + +// Don't force the documentation of the template parameter if it's a used type in the parameter list +unittest +{ + StaticAnalysisConfig sac = disabledConfig; + sac.properly_documented_public_functions = Check.enabled; + + assertAnalyzerWarnings(q{ +/++ +An awesome description. + +Params: + r = an input range. + +Returns: Awesome values. ++/ +string bar(R)(R r){} + }c, sac); + + assertAnalyzerWarnings(q{ +/++ +An awesome description. + +Params: + r = an input range. + +Returns: Awesome values. ++/ +string bar(P, R)(R r){}// [warn]: %s + }c.format( + ProperlyDocumentedPublicFunctions.MISSING_TEMPLATE_PARAMS_MESSAGE.format("P") + ), sac); + + stderr.writeln("Unittest for ProperlyDocumentedPublicFunctions passed."); +} diff --git a/src/dscanner/main.d b/src/dscanner/main.d index 3f7b304..8875797 100644 --- a/src/dscanner/main.d +++ b/src/dscanner/main.d @@ -476,7 +476,7 @@ string getConfigurationLocation() /// Patch the INI file to v0.5.0 format. //TODO: remove this from v0.6.0 -bool hasWrongIniFileSection(string confiFilename, bool patch) +bool hasWrongIniFileSection(string configFilename, bool patch) { import std.string : indexOf; import std.array : replace; @@ -486,12 +486,12 @@ bool hasWrongIniFileSection(string confiFilename, bool patch) static immutable v1 = "analysis.config.StaticAnalysisConfig"; static immutable v2 = "dscanner.analysis.config.StaticAnalysisConfig"; - char[] c = cast(char[]) readFile(confiFilename); + char[] c = cast(char[]) readFile(configFilename); try if (c.indexOf(v2) < 0) { if (!patch) { - writeln("warning, the configuration file `", confiFilename, "` contains an outdated property"); + writeln("warning, the configuration file `", configFilename, "` contains an outdated property"); writeln("change manually [", v1, "] to [", v2, "]" ); writeln("or restart D-Scanner with the `--patchConfig` option"); result = true; @@ -499,8 +499,8 @@ bool hasWrongIniFileSection(string confiFilename, bool patch) else { c = replace(c, v1, v2); - std.file.write(confiFilename, c); - writeln("the configuration file `", confiFilename, "` has been updated correctly"); + std.file.write(configFilename, c); + writeln("the configuration file `", configFilename, "` has been updated correctly"); } } catch(Exception e)