you could use a Function you pass the "seed" range to and returning a range from passed one to the last not empty cell in the same column, as follows (explanations in comments)
Function GetRange(rng As Range) As Range
With rng.Parent ' reference passed range parent worksheet
Set GetRange = .Range(rng, .Cells(.Rows.Count, rng.Column).End(xlUp)) ' return referenced sheet range from passed range to passed range column last not empty cell
End With
End Function
to be used as follows:
Sub Test()
GetRange(Worksheets("Data").Range("O2")).Copy
End Sub
you could enhance the function and have it handle a given "final" row
Function GetRange(rng As Range, Optional finalRow As Variant) As Range
With rng.Parent ' reference passed range parent worksheet
If IsMissing(finalRow) Then ' if no "final" row passed
Set GetRange = .Range(rng, .Cells(.Rows.Count, rng.Column).End(xlUp)) ' return referenced sheet range from passed range to passed range column last not empty cell
Else 'else
Set GetRange = .Range(rng, .Cells(finalRow, rng.Column)) ' return referenced sheet range from passed range to passed range column cell in give "final" row
End If
End With
to be used as follows:
Sub Test()
GetRange(Worksheets("Data").Range("O2"), 2).Copy
End Sub
having kept "final" row as optional, the function can be used with or without passing it:
Sub Test()
GetRange(Worksheets("Data").Range("O2")).Copy ' this will copy worksheet "Data" range from row 2 down to its column "O" last not empty row
GetRange(Worksheets("Data").Range("O2"), 3).Copy ' this will copy worksheet "Data" range from row 2 down to row 3
End Sub