private List<Parameter> getParameters(Function toFunction, ToolOptions markupOptions,
			boolean forceApply, boolean useCustomStorage) throws InvalidInputException {

		// See what options the user has specified when applying parameter names.
		VTMatchApplyChoices.ParameterDataTypeChoices parameterDataTypesChoice =
			markupOptions.getEnum(VTOptionDefines.PARAMETER_DATA_TYPES,
				DEFAULT_OPTION_FOR_PARAMETER_DATA_TYPES);

		boolean onlyReplaceUndefineds =
			(parameterDataTypesChoice == ParameterDataTypeChoices.REPLACE_UNDEFINED_DATA_TYPES_ONLY);

		Program toProgram = toFunction.getProgram();
		Parameter[] toParameters = toFunction.getParameters();
		int toCount = toParameters.length;
		int parameterCount = parameterInfos.size();
		List<Parameter> parameters = new ArrayList<>();
		for (int i = 0; i < parameterCount; i++) {
			ParameterInfo paramInfo = parameterInfos.get(i);
			SourceType source = SourceType.DEFAULT;
			String name = source != SourceType.DEFAULT ? null : paramInfo.name; // defer setting non-default name
			String comment = paramInfo.comment;
			if (!forceApply && i < toCount) {
				name = toParameters[i].getName();
				source = toParameters[i].getSource();
				comment = toParameters[i].getComment();
			}
			DataType fromDataType = paramInfo.dataType;
			DataType toDataType = (i < toCount) ? toParameters[i].getDataType() : fromDataType;
			DataType dataType;
			if (forceApply) {
				dataType = fromDataType;
			}
			else if (parameterDataTypesChoice == ParameterDataTypeChoices.EXCLUDE) {
				dataType = toDataType;
			}
			else {
				dataType =
					getHighestPriorityDataType(fromDataType, toDataType, onlyReplaceUndefineds);
			}
			VariableStorage storage = VariableStorage.UNASSIGNED_STORAGE;
			if (useCustomStorage) {
				if ((i < toCount) && (dataType == toDataType)) {
					// FIXME! This can result in a storage collision!
					storage = toParameters[i].getVariableStorage();
				}
				else if (paramInfo.storage != null) {
					storage = VariableStorage.deserialize(toProgram, paramInfo.storage);
				}
			}
			// Use LocalVariableImpl so we can set source type
			Parameter param = new ParameterImpl(name, dataType, storage, toProgram, source);
			param.setComment(comment);
			parameters.add(param);
		}
		return parameters;
	}
@param toFunction the function whose signature we are setting.
@param markupOptions the options indicate what parts of the function signature to apply.
@param forceApply true indicates that the function signature should be applied even if the
function signature, return type, parameter data type or parameter name options are set
to "do not apply".
@throws VersionTrackingApplyException if all desired parts of the function signature couldn't be applied.
Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionSignatureStringable.java