@Override
	public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) {

		ReferenceManager mgr = program.getReferenceManager();
		Listing listing = program.getListing();

		AddressSet funcStarts = new AddressSet();

		int count = 0;
		long initial_count = set.getNumAddresses();
		monitor.initialize(initial_count);
		AddressSet leftSet = new AddressSet(set);

		//
		// gather up all the locations that are referenced as calls
		//
		AddressIterator iter = mgr.getReferenceSourceIterator(set, true);
		while (!monitor.isCancelled() && iter.hasNext()) {
			Address addr = iter.next();

			count++;
			if (count > NOTIFICATION_INTERVAL) {
				leftSet.deleteRange(leftSet.getMinAddress(), addr);
				monitor.setProgress(initial_count - leftSet.getNumAddresses());
				monitor.setMessage(analysisMessage + addr);
				count = 0;
			}

			Instruction instr = listing.getInstructionAt(addr);
			if (instr == null) {
				continue;
			}
			if (!instr.getFlowType().isCall()) {
				continue;
			}
			Reference[] refs = mgr.getFlowReferencesFrom(addr);

			for (Reference ref : refs) {
				if (ref.getReferenceType().isCall()) {
					//
					// get the body
					//   if body has more than one entry point, don't create it
					//
					Address entryAddr = ref.getToAddress();

					// ignore calls to functions that occur immediately after the call
					//
					if (fallthroughCall(program, ref)) {
						continue;
					}

					funcStarts.addRange(entryAddr, entryAddr);
				}

			}
		}

		//
		//  Create functions at all the locations that are called
		//
		// remove any addresses that are already functions
		SymbolIterator symIter =
			program.getSymbolTable().getSymbols(funcStarts, SymbolType.FUNCTION, true);
		AddressSet funcEntryPoints = new AddressSet();
		while (symIter.hasNext()) {
			Symbol funcSymbol = symIter.next();
			if (isPlaceHolderFunctionThatShouldBeFixed(program, listing, funcSymbol)) {
				continue;
			}
			funcEntryPoints.addRange(funcSymbol.getAddress(), funcSymbol.getAddress());
		}
		funcStarts.delete(funcEntryPoints);

		// if only creating thunks
		if (createOnlyThunks && !funcStarts.isEmpty()) {
			// get rid of any functionStarts that aren't a thunk

			AddressSet thunkStarts = new AddressSet();
			AddressIterator iterator = funcStarts.getAddresses(true);
			while (iterator.hasNext()) {
				Address entry = iterator.next();
				Address thunkedAddr = CreateThunkFunctionCmd.getThunkedAddr(program, entry, true);
				if (thunkedAddr != null) {
					thunkStarts.add(entry);
				}
			}
			funcStarts = thunkStarts;
		}

		if (!funcStarts.isEmpty()) {
			AutoAnalysisManager amgr = AutoAnalysisManager.getAnalysisManager(program);
			amgr.createFunction(funcStarts, false);
		}

		return true;
	}
Following the creation of instructions this analyzer searches for direct
call references and creates functions at the called locations.
Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/FunctionAnalyzer.java