I would like to suggest that ranking things using the rownum pseudocolumn in a subquery is at best a flawed approach, rarely delivering on the actual goal. For instance, do you want the employees with the top three salaries? Or do you always want up to three records? How does this equation cope with employees with identical salaries? Et cetera.
It is a better approach to use the analytic functions, define your explicit rankings in those functions and return the results that actually answer the question.
begin
for rec in ( select sq.*
from ( select e.employee_id
, e.first_name ||' '|| e.last_name as full_name
, e.salary
, rank() over (order by e.salary desc) as salary_rank
from hr.employees e ) sq
where sq.salary_rank <= 3
order by sq.salary desc )
loop
dbms_output.put_line('RANK ' || rec.salary_rank);
dbms_output.put_line(rec.employee_id || ' - ' || rec.full_name || ' - ' || rec.salary);
end loop;
end;