Rules / Maintenance

User-defined function nothing calls

info UDF_NOT_CALLED · built in · model layer · scope: Function

What it checks

User-defined functions that no measure, calculated column, calculated table, calculation item, row-level security filter, format string expression, KPI, or other function in the model calls. The functions of a DAX Lib package, which share one DAXLIB_PackageId annotation, count as one: the package is reported once, on its first function, when nothing outside it calls any of them.

Each finding names the function as the model does, as Local.AddVat, and points at its function line in definition/functions.tmdl. A package's finding says which package it is and how many functions it holds, as package DaxPatterns.AbcClassification: none of its 2 functions is called.

Example

Fires the rule
table Sales
	column Amount
		dataType: decimal
		sourceColumn: Amount
	measure 'Sales With Tax' = Local.AddTax(SUM('Sales'[Amount]))
		formatString: #,0

/// Adds 10 percent sales tax to an amount.
function 'Local.AddTax' = (amount: NUMERIC) => amount * 1.1

/// Adds 20 percent VAT to an amount.
function 'Local.AddVat' = (amount: NUMERIC) => amount * 1.2
After the fix
table Sales
	column Amount
		dataType: decimal
		sourceColumn: Amount
	measure 'Sales With Tax' = Local.AddTax(SUM('Sales'[Amount]))
		formatString: #,0

/// Adds 10 percent sales tax to an amount.
function 'Local.AddTax' = (amount: NUMERIC) => amount * 1.1

Why it matters

A function nothing calls is dead code that looks alive. It sits under Functions in Model explorer beside the ones in use, and anyone writing DAX in the model can find it and call it, with nothing to say whether it still does what its name promises or was left behind by a rewrite.

It also keeps other dead code alive. A hidden measure or column that a function names counts as used, whether or not anything calls the function, so UNNECESSARY_MEASURES and UNNECESSARY_COLUMNS pass over everything the dead function names. Deleting the function brings those to light.

A DAX Lib package installs all of its functions at once, so a model that calls one of them carries the rest unused as a matter of course. The rule reports a package only when none of it is called: the whole library was installed and never used.

How to fix it

Delete the function, after checking the callers pbiplint cannot see, listed under When to ignore it.

In Power BI Desktop's Model view, select Model at the top of the Data pane to open Model explorer, right-click the function under Functions, and choose Delete from model (Using Model explorer). In TMDL, remove the function's block from definition/functions.tmdl: its /// description lines, its function line, and the lines indented under it. For a package, delete each function that carries its DAXLIB_PackageId annotation.

When to ignore it

When something outside the model's own DAX calls the function:

Or when the function is kept on purpose, such as one written ahead of the measures that will call it.

To ignore this rule on one object, add annotation pbiplint.ignore = UDF_NOT_CALLED under the object in its TMDL file. Power BI Desktop keeps the annotation. To turn the rule off for a whole project, set "UDF_NOT_CALLED": "off" under rules in pbiplint.config.json.

Quirks

Check a model for this Improve this page