« first day (1971 days earlier)      last day (1209 days later) » 

00:00
RELOAD!
[rubberduck-vba/Rubberduck] 10 issue comments.
[Minesweeper] Games Played: 87, Bombs Used: 40, Moves Performed: 9995, New Users: 16
 
2 hours later…
02:12
0
Q: How to set the Attribute VB_PredeclaredId using RubberDuck '@PredeclaredID

Ricardo DiazAs a Rubberduck and OOP newbie I'm still learning how to use the software and take advantage of the programming techniques First time I saw about folder annotations (https://github.com/rubberduck-vba/Rubberduck/wiki/Using-@Folder-Annotations) it was quite intuitive how it worked. But then came ...

02:25
Surreal experience seeing RD questions on SO.
@ReinstateMonica welcome to the pond.
 
4 hours later…
07:22
0
Q: Changing all comments' size in range to autosize

Vba noobI have a code that turns all comments size to autosize, however as I have quite a few cells with comments, this code runs a little slow, to the point where it will become non responsive for awhile. Sub AutoSizeComments() Dim c as range, ws as worksheet Set ws = ActiveSheet 'Loop th...

 
4 hours later…
11:30
0
Q: Manage Excel Styles with VBA OOP Approach

Ricardo DiazI want to manage the Excel Styles in a more flexible way with bulk actions and at the same time improve my newly acquired OOP concepts. Objectives: Load the current Styles list (name and type=builtin or custom) in an Excel Structured Table (ListObject) Allow users to: Delete Duplicate (creat...

11:48
@Duga So now have a !
 
1 hour later…
12:49
@StackDuck Looking at the inspection image in that question:
The quick fix descriptions are a bit... vague... Adjust? what, exactly, does that mean?
IINM, it works both ways
e.g. if there's annotation but no attribute, then add attribute. If there's attribute but no annotation, then add annotation (or was it remove the annotation?)
How 'bout Adjust attribute annotation becomes Remove annotation from code text and Adjust attribute values(s) becomes Add (hidden) annotation to code (hrm... that's not quite as clear...)
In that case, @this, it could just say Adjust and let the duck figure out what needs to be done. "Adjust" is vague...
How 'bout: Remove visible annotation from code and Set flag based on annotation
the best thing would be to change the text based on what it'll do (that presumes that my recollection is correct, though. I might be wrong)
I don't use them all that often, but I have to really stop and think about which one I want each time.
I prefer that it's always explicit
12:54
Yeah, my point...
It only removes the annotation for those annotations for which the non-existence of the annotation means the same as false for the attribute.
Set attribute to FALSE condition, then - that sounds good to me.
ah so the behavior is different for different attributes.
something to make it more intuitively obvious and remove the cognitive load
well, the behavior is different anyway, isn't it? (not certain)
Adjust annotation changes the annotation to bring the annotation and attribute into sync and Adjust attribute achieves the same by changing the attribute valu(s).
12:56
Just seeing it there reminded me of just how unclear those 2 labels are. I'm not sure exactly what the wording should be
Maybe it is just me, but I find them completely clear.
but we're not really adjusting the annotation if that's selected - we're removing it from the code pane, right?
Add missing annotation and Update attribute to match annotation?
For those who are learning, though, it's not completely clear.
The quickfix displays a description that is not context aware.
12:58
seems to me it ought to be in this particular case since behaviors will change
For me removing is an adjustment as well as adding.
maybe the problem (i.e. lack of complete clarity) is that the error is "they're not in sync". Maybe if that was more specific: There's an annotation but no attribute or There's an attribute but no annotation or There's an annotation and an attribute, but they don't match
but I guess that'll be its own PR
Actually, adding is covered by another quickfix.
I dunno. I'm think of it from the perspective of the guy who's just learning about all this and doesn't yet have them completely groked and committed to memory.
13:00
There is an attribute in the screenshot.
Classes always have a predeclared id attribute.
The default value is 'False`.
I dunno... maybe because I've had my head in SQL all morning, not VBA - the sudden switch may have been too much of a shock to the brain so it's not all that clear. I do know that I really have to think about those 2 quick-fixes to really make sure that I'm selecting the right one when I do use them...
goes back to SSMS...
I think the only problem is that "attribute annotation" would be clearer it if just said "annotation"
i.e. "Adjust attribute" vs "Adjust annotation"
Perhaps even "Adjust hidden attribute" and "Adjust annotation comment"
Adjust attribute's annotation?
nah it's having the word "attribute" in there that's confusing
there's still the problem that Adjust can do different things depending on the context, though.
13:08
right. unclear if it's going to add it or remove it
I'd rather that it said either Add annotation or Remove annotation or Sync annotation's value with attribute's value as appropriate
but that needs a PR to enable context-aware menu, I guess.
13:34
It will never add.
It either changes the annotation value, removes the annotation or replaces it with another one.
I subsumed everything under the term adjust.
As our resident Canadian, @MathieuGuindon, you'll find this amusing. (least, I think you will)
I just received an email from the "Public Affairs Officer, Garrison Petawawa Canadian Armed Forces / Department of National Defence" about their email bingo game to raise funds for Eastern Ontario United Way
I'm not now, nor have I ever been in the Canadian military, so I'm not entirely sure why they have my email address...
The grand prize is 62 Canadian dollars, so maybe I should just play. If I win, I could take the spouse out for a chicken dinner!
@FreeMan Obviously you are now in Canadian military.
it seems so.
I'm sure you'll look fetching with the red shirt, the ranger hat and your standard issue moose.
Oddly, almost all the addresses in the To: list (no, they didn't BCC everyone for security, that would be obvious) are all @forces.gc.ca email addresses. There are a few gmail.com, hotmail.com and yahoo.com` addresses scattered in.
13:40
@this Feel free to implement the logic for the context sensitive quickfix label.
I hate wearing a hat - it leaves me with hat head.
keep wearing it?
Can I customize my standard issue moose? There's a Pep Boys not too far from me...
that'd be a sight - getting them to get a moose on the car lift
Oh, wait, this is the best part!
If this is truly spam, it's got to be one of the best crafted spam messages in the history of the internet. None of the horrible misspellings or terrible grammar, no pleas for urgent action, no clickable links beyond the senders email address in the signature (in English and French, of course).
and a complete set of BINGO numbers to get my card started!
13:45
hm, or maybe that is legitimate thing but they goofed up the email address?
most likely. I've been getting a fair number of those lately.
poor Canadian FreeMan. He's missing out on lot of things....
Sigma Xi has [email protected] in their database. Somewhere along the lines, the periods are getting removed and I'm getting the mail. I know that's a legit organization, so I've unsubscribed several times (which isn't working) and I've emailed several of their "contact us" links, though none are really in a technical area. I'm still getting them.
14:24
@FreeMan that's... worrying.
It's more common than I'd like, TBF.
Pretty sure DoD would consider it a security breach
even for a bingo game? That might not be a DOD thing but informal thing among buddies for all I know.
(presuming it's a real thing)
not the bingo, but the @forces.gc.ca email addresses
Right but if it's an event organized by buddies emailing to each others' work emails, would they get kicked? (I'd think so.)
14:33
if these were my staff I'd call a meeting and ternly teach them how to use BCC
14:43
> **Rubberduck version information**
Version 2.4.1.4627
OS: Microsoft Windows NT 10.0.18362.0, x64
Host Product: Microsoft Office x86
Host Version: 16.0.11328.20438
Host Executable: MSACCESS.EXE

**Description**
ToDo ignores '@' marker

**Screenshots**
![image](https://user-images.githubusercontent.com/1339532/67777839-3c77de00-fa5a-11e9-8387-0a95a09dcd64.png)

**Logfile**
[RubberduckLog.txt](https://github.com/rubberduck-vba/Rubberduck/files/3784102/RubberduckLog.txt)
Interesting. That's 2nd? 3rd? issue relating to the markers in Todo explorer.
@MathieuGuindon @this TBF it's probably something organized by the "family relations committee" (or something similar). My son's in the US Army and every post has all sorts of stuff like that they put on to help make it a bit easier on the families. It's not shocking to see non-DOD email addresses, since often times spouses are on the list.
The lack of BCC, though, is concerning.
@this at least people are using it!
Yeah, it's usually those informal outings that'd be source of leaks
Ok, I'm curious - normally I don't like doing this stuff but in this case, I want to do a "best-efforts" but accurately report the result.
Dim Result As Boolean

Result = True
For Each foo in bar
  If baz() = False Then
    Result = False
  End If
Next
The baz() is a library method so I can't change the definition. Can we do better than that?
Note further that it doesn't short-circuit at first failure. That's OK and we do want that.
15:09
defaulting to success feels risky?
(also, you lose all but the last failure)
#ItDepends
I take issue at If {bool-expression} = False ;-)
@this this is just an early exiting version of And-ing all of these together
Dim Result As Boolean
Result = True
For Each foo In Bar
  Result = Result And baz()
Next
I don't recall whether that is semantically equivalent though...
because And may not be short circuiting ...
it's not short-circuiting
the end result is the same: you iterated all of bar and only return true if all bazzed
@MathieuGuindon ohhh right because there's no early exit in the original either ..
15:14
presumably baz actually takes at least one argument
return Bar.All(foo => baz());
yes, that's what I'm after, basically
no early exiting is OK in this circumstance. It would look very different if that was a requirement.
and yes, defaulting success isn't really something I like doing.
I suppose I could flip it around and call Dim HasFailed As Boolean but....
@MathieuGuindon Not sure I follow how I miss out on the last failure.
Going to use Vogel's And. More succinct.
@this you don't, that was me misreading
Word's OM is a joy to work with.
@this cue anchorman bob's "I don't believe you" gif
@Duga Wow... that's an old build! However, I do get a repo on .5105. And, I've ensured that I've got @ added to the TODO marker list in settings.
15:30
> this is probably an issue around tokenization in the lexer making the @ unavailable for the marker search...
the trouble is that the @ is used to make annotations. I don't think it can be used.
(what Vogel sez)
What they really want is annotation explorer
@this with ad-hoc annotations?
no but it'll serve as a cluestick that @ is special.
how about validating TODO markers, and forbidding @?
@QuackExchange Mug's gathering a following. <whisper>Industry Disruptor</whisper>.
15:41
forbid how exactly?
not valid -> refuses to save/update the marker
> TODO markers must consist of alphanumeric characters.
oh via the settings
ok was thinking via code panes, thus the confusion
looks who needs a cluestick...
16:18
If I have an int Identity(1,1) column in my table, I know that I cannot insert values into it (without specifically enabling that). When I do an INSERT INTO that table, if I'm providing values for all the other columns, do I need to specify the columns, or can I simply use the implicit *?
Whether or not I should specify columns out of completeness/OCD/whatever isn't the question
TBH, * is risky
it's OK in an one-off script but otherwise, it's very fragile
you're better off explicitly enumerating the columns in the insert & select lists.
This is an initial population of configuration tables, so I should be good to go. I just wanted to be sure that I didn't have to specify column names since I've already written about 40 inserts w/o them...
I'll do so as I move forward, but these are a large group of individual INSERT each with different sets of data and looking things up in other tables. I could go back to paste the columns in, just don't wanna if it's not required
even in an administrative script, it's not foolproof (esp. if you need to repeat it for a different database, whatever). A shortcut I use is to use Script As -> INSERT, delete the VALUES part, then use Script As -> SELECT and paste it then remove the identity & rowversion, then done.
wait, I don't think you can even implicitly * if your table has either identity or rowversion since the * will include them and backfire.
16:33
ah, "too broad" is now "needs more focus". neat.
> Update

Turns out this was user error (DERP) on my part, I had assumed that the parser had completed but it was stuck at pending. Once I clicked it to refresh everything works as it should.

Kevin
@this that was the crux of my question. I'll go insert them all. It's just a simple copy/pasta, so not difficult, just annoying
@Duga better than
probably a dumb idea but if the menus had a new menu item Reparse to enable menu or something would that help in the UX?
didn't we add a "Refresh" command at the top of the RD menu?
no i meant on the context menu
#BiggerCluestick
let's make a neon-sign flashing arrow that points to the "pending" refresh button instead.
#problemsolved
17:01
:D
17:13
or we find the correct sequence of magic incantations and ritual blood sacrifices to allow parsing on startup
I don't see that happening until we can cut down the parsing time. It's one thing to wait ~10 seconds but when it's a minute on a cold cache...
17:33
how about a prompt?
I don't like prompting because it gets obnoxious, but we could use it to tell the user what to do if they don't want to parse right away
I hate prompts
that's why I was thinking about a pseudo-menu item on the bottom.
other, I don' thave any better suggestion short of going to that mythical avalon code pane
MentionOfAvalon++;
Avalon has turned into our glass door, did it
feels like it. There's also the LSP thing
but avalon definitely has been talked about for so long
@MathieuGuindon you mean the thing we walk into over and over?
I think, to support the LSP thing, we should think about splitting Parsing into a piece for the symbols and one for the engine, possibly with the (actually not so large) parsing API extracted as well.
@M.Doerner that hard-coded 2 confuses me... I can tell from the tests that it's apparently correct, but I'm puzzled nonetheless.
@MathieuGuindon We deal with index 1 a few lines above.
maybe give it a constant name instead?
e.g. FromExpression and ToExpression. IDK.
basically the goal here is to iterate all expression that are after the NEXT token, right?
19:06
This would be so helpful. Just yesterday and today, I opened my Excel1 (initialized RD),
- then Excel2 (1. parse),
- then I deleted 2 modules (2. parse),
- 1 userForm (3. parse),
- then worksheet (4. parse)
- then I copied worksheet from Excel2 into Excel1 (5. parse)
- then I closed Excel2 (6. parse)

I know I can do this before I click on "Pending..." but if I'm in the middle of work and my parse takes 40 seconds, I don't want to reopen Excel.

Small checkbox "Auto-Parse" would be good. Until it's fast enough.
@SonGokussj4 that isn't what I suggested though. I was talking about the initial parse.
if RD is loaded, it will respond to a VBProject being loaded or unloaded, or to a VBComponent being added, removed, or renamed.
Oh ok. What a pity :-)
if we put that behavior behind a toggle-switch, what power users gain in performance will be vastly outweighted by new users losing code and (rightfully) blaming RD for it
the solution is to do things faster and smarter, not to stop doing these things
19:40
most excellent! In SSMS, when you split the pane to view 2 different parts of your code, Ctrl-Shift-Up and Ctrl-Shift-Down will jump you from split to split!
@MathieuGuindon were you coding in VB4/VB5 back then? Do you remember the transition from 16-bit to 32-bit?
uh, I was 15 or 16 and used drag-and-drop to make a "launcher" app for Duke Nukem 3D in VB4... beyond that... no memories :)
lol ok
someone was complaining that 16-bit to 32-bit transition was transparent but I don't think that's correct. Hence my question.
It might have been less painful in comparison to 32-bit to 64-bit but certainly not transparent as I'm pretty sure all pointers had to be declared as Integers back then.
uh, how does one lights up the Implement Interface refactoring again?
20:03
Looks like the UK is getting an election in December :-) Things might finally start happening...
> @Vogel612 Sorry! I've been having trouble running the project. Perhaps someone else wants to take a shot? Don't have a lot of time ATM.
Dear Santa, I've been (mostly) good, can I please have a Brexit for Christmas?
@mansellan because I'm an ignoranus, how exactly does it work? Didn't you just elect Trump-clone?
Heh, no. Boris is also love-him-or-hate-him, but that's where the similarities end
He's been gunning for Brexit, but doesn't have the numbers in Parliament to do, well, much of anything really.
Hence the election. If he gets a decent majority, it's full steam ahead. If the opposition do or its another hung Parliament, well, God help us...
I did hear that Canada just re-elected Trudeau though
lol
20:21
Everyone is unhappy with the pineapples representing their respective countries.
Even those that vote for them find out later they made a mistake.
IDK. Whenever I talk to them about their votes, they usually respond it's basically their holding their noses.
In theri mind, they're voting for the lesser of 2 evils
But in reality, the next round, they face 2 evils even more evil than the original 2 evils so they end up voting for evil either way
I’d rather vote for a duck at this pond. There’s decency and scruples found amongst members.
I know that COALESCE will take the first non-NULL value from the list provided. Is there a similar function to COALESCE(ColA, ColB), where if ColA is NULL, have it return Fixed Text instead of the actual value of ColB, or is that some code/write your own function?
20:57
Lunch tasted extra good.
I should eat it more often.
@this As far as I remember from Raymond Chen's book, when they did the move to 32 bits, they retained all old APIs and used dword for everything 32bit and just word for 16bit.
There was no implicit resizing.
Hm. The API has pointers, though. That would resize, no?
The API did not take pointer types but just word or dword.
I'm confused. I thought they always had void * since forever.
OK, that exists.
21:02
so when they define various Windows data types as LPXXXX, it eventually maps to void *
However, AFAIR, the 16bit API took words for all kind of things.
but since VB* did not have a true pointer data type, those had to be mapped as Integer originally, then converted to Long.
I see.
Is the default member access inspection correct? bar = fooSheet.Cells(inputRow, inputColumn).Value2 is coming up as a result. I'm not using the Excel.Range._Default member since I'm explicitly using the Value2 member. What am I missing?
Well, there also is that they introduced a completely new API for 32bit.
You really do not want to do that again today.
That would probably explain why there is so little VB5 code transitioned to VB6 code. At least the API stuff.
21:05
@IvenBach It is the indexed one, right?
Indexed one?
ÌndexedDefaultMemberAccessInspection
Yes. That is the inspection.
You might want to have a look at the documentation for Range.Cells, which I extended two weeks ago.
To make it short, Cellsreturns a range and has no parameters.
Cells(a,b) is Cells.Item(a,b).
The result is correct and only a hint since default member access for collection access is an understandable and quite transparent use case.
Is that the same for Range, Range.Columns, Range.Rows and pretty much everything else that returns a range object?
I have a braintickle telling me you let me know this already.
21:10
Actually, Cells(a,b) is Cells.[_Default](a,b), but that forwards to Item.
Yes, and it is in the documentation, now.
Cells just changes the internal state of the range to consist of single cells.
hmm weird - it says it's merged but this still seems to be the old
^ I've been reading the commit to better grok your examples.
Argh, I did not read far enough. It is not transferred yet.
Your example for columnsRange with the 3 explicit Debug.Print was very helpful.
It is merged to master, but they only merge master to life at distant points in time.
Basically, their master is our next.
21:19
i have to say that it's kind of confusing
If you're not expecting that behavior.
I usually think of master as the primary trunk and the next / dev / whatever you call it as the growing stem
Never realized how pervasive default member access still is. Even after learning what it is and trying to avoid it I have a lot.
21:34
I think it is OK for collection access as long as you are aware that it happens.
The completely implicit default member access fot Let coercion is the evil one.
The danger is "as long as you are aware that it happens." and it wasn't realizing it was happening. Even with .Cells() I wasn't understanding it.
That is really the most confusing one, along Columns and Rows.
Range.Columns.Item(RowIndex:= ,[ColumnIndex:= ]) is doubly confusing since RowIndex isn't required.
Yes, RowIndex is the relative column index here.
Range.Columns.Item("2:3") barfs while Range.Rows.Item("2:3") is perfectly fine.
21:40
Oh, you can do that?
Then my changes on the documentation are still uncomplete.
Range.Columns.Item(2).Resize(ColumnSize:=2) is the workaround I've found for that difference.
You can do Range.Columns("B:C").
don't they always have a R1C1 alternate?
Not for this.
@M.Doerner WTF... I would have never thought of doing that. #TIL.
21:42
how fun
At least, as far as I know.
Excel is stranger than I realized.
See, I thought it only works for the Columns one.
It kinda-sorta make sense.
In my ignorance, I did write a routine to convert a number into a letter for accessing the column. It really chafes me that I had to do it myself when it should have been provided by Excel OM.
21:43
You surprised me with the Rows equivalent.
(of course, now that I've written this, I'll probably learn about a super secret sauce way of converting a number to letter)
docs.microsoft.com/en-us/office/vba/api/… was my derp moment when I found out about it.
um. That presupposes there's a formula, doesn't it?
(i'm just using it for addressing)
I only found out about the Columns version when rewriting the documentation for Item.
but I guess I could just wrap it with "=" & whatever, convert it then strip the =.... Clunky.
21:49
^ I know it's not always applicable.
I use formulas with cell references so for me it was a no-brainer.
That's an example of one responsibility too much right there
How so?
well, I wanted a conversion from A1 to R1C1 style (or vice versa)
but to use that function, I have to make a formula, then strip it away to get the result I wanted.
Yeah.
Now I see what you're saying.
in actuality, I should be calling Application.ConvertColumn(), whatever
and the Application.ConvertFormula() could then call the Application.ConvertColumn() as part of its conversion.
e.g. do one thing only and do it well.
but it's OK! Excel OM is totally intuitive! Fun! Simple!
21:57
I think Rows, Columns and Cells should really return Ranges and Item should always work as for single cell ranges.
However, that will never change.
Regarding a For Each cell in FooBarArea depending on how FooBarArea is originally set that determines whether it's looping through each Range.Row, Range.Column, or Range.Cells?
Yes
If it comes from someRange.Columns, it will iterate over the columns, for someRange.Rowsover the rows, both times only over the first area, and for someRange.Cells`over all individual cells.
Quite enlightening if you're not expecting that. So many times I've done set foo = BarSheet.Range("A1:D15") without realizing implicitly .Cells was part of the For Each loop.
That is why it makes sense to always iterate over FooBarArea.Cells if you want individual cells.
32 mins ago, by IvenBach
The danger is "as long as you are aware that it happens." and it wasn't realizing it was happening. Even with .Cells() I wasn't understanding it.
22:09
No, there is no implicit cells.
Now I am though.
If it's omitted it's implied, as far as I've understood.
Worksheet.Range just returns a range consisting of single cells.
No, you can save soemRange.Comlumnsin a range variable and iterate over it later.
That would iterate over the column ranges.
The kind of iteration behaviour and behaviour of Item is internal state of the range.
Yes, exactly.
I'd never understood all that adequately before to understand it correctly.
22:12
BTW, this is bad API design.
Because .Item get's really confusing and isn't actually doing what its claiming?
You basically have subtypes of Range violating the Liskov substitution principle.
Ah. You can't substitute a range object that a fooRange.Columns for a bar.Range.Rows and get the same behavior.
Whenever you see a range, Item should do an equivalent thing for the same parameters supplied.
^^Exactly
I also was not aware of this until recently.
@M.Doerner :click: That's what you meant.
22:14
I always just took ranges as parameters and never guarded against such differences.
Neither have I.
Then again I'm really the only one using my own code.
When the methods are only private, i is OK, because you will probably not produce bad ranges in your own code in the module, but for public ones, it might make sense to guard against it.
Well, thinking about this strange behaviour of Range I think one understands why there is the L in SOLID.
The most stupid thing about this mess is that supplying both arguments to Item for a range obtained via Columns actually causes an error.
Public Sub TestingVariousRanges()
    Dim sourceArea As Range
    Set sourceArea = Sheet1.Range("A1:J7")

    Dim rowRange As Range
    Set rowRange = sourceArea.Rows

    Dim columnRange As Range
    Set columnRange = sourceArea.Columns

    Dim cellsRange As Range
    Set cellsRange = sourceArea.Cells

    PrintNthItem rowRange, 1
    PrintNthItem columnRange, 1
    PrintNthItem cellsRange, 1
End Sub

Private Sub PrintNthItem(ByVal printRange As Range, ByVal NthItem As Long)
    Debug.Print printRange.Item(NthItem).Address
@M.Doerner ^ That returned the results I was expecting. Thanks for this enligtenment.
You are welcome!
How does one test for these?
Whether it's returning a multi row/column span or just a single cell?
22:24
Well, you can look at what Item returns.
I do not hink that there is a property exposing it.
One way to test it is to try to access Item(1,1) and conclude that it not a cells range, if there is an error 1004.
Note that Item with one parameter acts in an unexpected way for multi-area ranges.
I am so glad you can just download language packs for Office for the newer versions.
I had to reinstall my sysyem and the home use program version of 2019 installed in German for some reason.
Excel in any other language than English is just confusing.
22:47
@M.Doerner You have an example of that. My naive attempts failed.
Debug.Print Application.Union(Sheet1.Range("B2:C3"), Sheet1.Range("Z14:AA15")).Item(6).Address
You mean $C$4?
Yes, I extended the wrong way.
That will never come up, if you iterate over the range.
Then, element 6 will have address $AA$14.
Just saw that.
Item just only uses the first area and extends it to the bottom of the sheet.
22:55
That's the behavior I've always seen. And sort of expect now that I'm thinking about it.
Public Sub ButWhy()
    Dim sourceArea As Range
    Set sourceArea = Application.Union(Sheet1.Range("B2:C3"), Sheet1.Range("Z14:AA15"))

    Debug.Print sourceArea.Item(6).Address & "|" & sourceArea.Areas(1).Item(6).Address

    Dim cell As Range
    For Each cell In sourceArea
        Debug.Print cell.Address
    Next
End Sub
mumble mumble :shrug: is best I can offer.
Storing a rowRange/columnRange in another range object causes it to be a cellsRange.
Enum RangeType
    NotSet
    RowRangeType
    ColumnRangeType
    CellsRangeType
    MultiAreaType
End Enum

Public Sub TestingVariousRanges()
    Dim unsetArea As Range
    Debug.Print DetermineRangeType(unsetArea)

    Dim singleArea As Range
    Set singleArea = Sheet1.Range("A1:J7")

    Dim rowRange As Range
    Set rowRange = singleArea.Rows

    Dim columnRange As Range
    Set columnRange = singleArea.Columns

    Dim cellsRange As Range
    Set cellsRange = singleArea.Cells

    Debug.Print DetermineRangeType(rowRange)
@M.Doerner ^ if that helps you with any of your checking.
You've now made me paranoid to trust any Range objects without explicitly adding .Rows | .Columns | .Cells.

« first day (1971 days earlier)      last day (1209 days later) »