dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
18.9k stars 4.01k forks source link

Proposal: Highlight branching keywords in IDEs #18810

Closed lachbaer closed 5 years ago

lachbaer commented 7 years ago

Copied from dotnet/csharplang#458

Summary

All branching keywords like break, continue, goto and most of all return should be highlighted in a different color as the standard keyword color.

In Visual Studio and VS Code my suggestion is violet instead of blue.

Motiviation

Branch points should be visible in a method without any further ado for the reader. No marking of one keyword and get all equivalent keywords highlighted should be required.

Variations

breaks in switch statements are expected and belong to the constructs normal appearance. They could be kept in the standard highlight color.

branchhighlight

jjvanzon commented 7 years ago

bigown:

if is branching, foreach is branching, many keywords branching.

lachbaer:

I - obviously - mean the mentioned statements

@lachbaer: At first I liked the extra highlighting of control flow statements you proposed, but soon I thought what bigown thought: many keywords are branchings. It soon becomes subjective, which control flow statements are important enough to be highlighted. I am pretty sure it is not hard to write an analyser that formats those keywords for you. I think I saw a video once where they let a piece of code blink with flashy colors and gradients and everything in response to an analyser. Google away. Since you are looking for something so specific in the code, the analyser does not have to be hard, just let it look for specific keywords. Analysers become harder when it has to scan around to find the context in which something in code happens, but this is just every occurrence of a specific set of keywords, so it should be doable. If you haven't written one before: it is fun to have done it at least once. I lost interest when I wanted to write a complicated one and found I had other things to do with my time. But they're pretty cool.

Pilchie commented 7 years ago

Related to https://github.com/dotnet/roslyn/issues/3976.

lachbaer commented 7 years ago

There is this (ongoing) discussion in the world whether premature exits from loops and methods should be strictly avoided or not. That discussion is as old as programming languages are but still comes up in every programming class.

One of the reasons is that those 'jumps' can easily be overseen. That may date back to days of monochrome monitors. Because they could be overseen, it is considered bad style to write code that way. (Of course there are many more arguments for and against it.)

We can counteract against that discussion by simply highlighting the "offensive" keywords by default to everybody. If the cannot be overseen any longer it will take wind out of the sails of this discusion and maybe the one or other programmer just feels better when doing what is actually a standard procedure in C# and VB.

jjvanzon commented 7 years ago

From your story I infer, you do not want to highlight all branching keywords, just the exiting and jumping ones.

Perhaps this does not require an explanation, but I like to say, that early returns, breaks and continue prevent excessive nesting. So they can keep things overviewable. So that's an argument in their favor. Also, you can combat the problem of overlooking the statements, by splitting things up into shorter methods. The shorter methods can then take even more advantage of early returns.

Because the exiting statements are so important, I tend to keep them on their own line with a blank line before it, or in their own block. I hardly ever write:

if (myCondition) return;

I would write it like this:

if (myCondition)
{
    return;
}

So I understand now, why you want to highlight these keywords in particular.

lachbaer commented 7 years ago

@janjoostvanzon Everybody has their own style. Whereas I totally understand (!) the neccessity of braced blocks and putting everything on its own line, line in your sample above, I absolutely do not like all the empty space and additional lines that style produces. It leads to unneccessary scrolling, in particular on smaller screens, and thus harder to understand - or in case it's my own - to verify code. You could argument that all programmers should afford themselves a big screen for coding, but it is not only about the coding. I frequently read posts and articles on my smaller and mobile devices. I find it cumbersome if the code is exploded because of putting braces on single lines, making me scroll up and down and up and down and up...

As you said, you do it, inter alia, to make sure that those statements aren't overseen. I bet, most others have the same argument! Count me to them, too.

My hidden hope is that when we start highlighting the exiting and jumping keywords by default { I forgot to mention throw and yield } it will mid- to long-term establish kind of a markup standard, because of the broad distribution of VS/VSCode, not only for Roslyn languages.

A side effect is that it will then become easier to write if (myCondition) return; because overseeing it is nearly impossible. And practically code highlighting is everywhere nowadays, monochrome representations are the concrete minority. (Same counts for indentation. Which seriously taken presenters write or show randomly indented code today? There shall be languages out there that even rely on proper indentation 🐍 😉 )

miloush commented 7 years ago

Note that the keywords are already highlighted when you place the cursor on any of them.

More importantly, only those relevant for the current block are highlighted, I would be a bit worried mixing highlights from different levels. Even your example, by not highlighting the //TODO break the code gives impression that only flow related to the for is highlighted, but that is not what you proposed, right?

Either way, I would prefer this to be part of #3976 rather than a separate feature.

lachbaer commented 7 years ago

@miloush

Note that the keywords are already highlighted when you place the cursor on any of them

That will not help by "overflying" the code, i.e. by just reading it. Also in documentary blogs there is no way for interaction with the code.

but that is not what you proposed, right?

Actually, yes. break of a switch belongs to it as ketchup does to fries. There is no switch without break. But break in a for is already an exception and rather seldom.

I would recommend just a slight color difference to these keywords anyhow! Maybe even more purple than violet. They shall not completely jump in the eyes, just be slightly quicker to spot without focusing on the single letters/words (eyes get worse when you get old 😉

miloush commented 7 years ago

Actually, yes.

Let me reverse the question to make sure we understand each other. If the switch contained goto, would you highlight that? If there was another for inside the switch, would you also highlight its break?

lachbaer commented 7 years ago

Yes. Think of the switch'es break as being another keyword, e.g. endcase. Or the loops break as exit. The main difference between them is that a break in a loop is unexpected - you expect the block to be executed top down and not being interrupted - whereas switch, as being just "syntactic sugar" for consecutive else if's, is expected to jump after a case. Unexpected vs. expected.

goto is always an unexpected jump. I consider continue also as always unexpected even when being the last statement in a loop.

CyrusNajmabadi commented 7 years ago

There is no switch without break.

I don't know what this means. There are lots of switches without "break" :)

The only rule is that a switch label cannot 'fall through' to the next label, unless it's empty. However you accomplish that (i.e. return, break, throw, goto, and even continue) doesn't really matter.

CyrusNajmabadi commented 7 years ago

The main difference between them is that a break in a loop is unexpected

I don't know what this means either. It's totally standard and expected for tons of loops to break.

you expect the block to be executed top down and not being interrupted

I don't know why you would expect this. It's like saying "i don't expect a method to return in the middle". Such an expectation is certainly domain specific. i.e. you may code this way, but in no way is it any sort of standard practice.

Unexpected vs. expected.

I think you have a very specialized view of code in general (and C# in specific). I think you're looking for the team to design the experience around your specialized view, whereas i would recommend that you provide this own level of customization for yourself as your needs are extremely specific.

lachbaer commented 7 years ago

@CyrusNajmabadi Don't take it too literally. When you read my comment above about 'expected' and 'unexpected' jumps you'll probably get it.

CyrusNajmabadi commented 7 years ago

I was literally responding to your post about expected/unexpected :)

I think i get it. But my point is that your view is extremely specific to you.

lachbaer commented 7 years ago

@CyrusNajmabadi I'm more than confused currently! Is my English so bad? On what you answer is absolutely (!!!!!!) not what I mean... It might seem as if, but my coding and view on it and C# is not that different. I was coding on eary 8 Bit CPUs. Maybe there is still this memory and performance "optimization" in my mind. But, really, I am speechless now and currently do not know how to describe differently what I mean.

lachbaer commented 7 years ago

@CyrusNajmabadi

I think i get it. But my point is that your view is extremely specific to you.

Funny, I learned about that in computer classes in school, even before I already read it in books, and had it told twice during my studies in two different faculties. VERY specific! :-/

CyrusNajmabadi commented 7 years ago

is absolutely (!!!!!!) not what I mean

Then what did you mean? If you can clarify that would be helpful. All i have to go on is what you've posted so far :)

but my coding and view on it and C# is not that different.

My point was that you appear to have some misconceptions about these C# constructs and how they work. For example, your views on break+switches is not accurate.

CyrusNajmabadi commented 7 years ago

Funny, I learned about that in computer classes in school

It could be specific to your school :) Up until now i don't think we've ever even heard a single person mention this despite interacting with thousands of developers.

About the only thing close to this is some schools of thoughts that prefer a single return point for a function. This was especially prevalent in the days of C where the language didn't have an easy way to cleanup resources when a function exited early.

--

Note: i see value in allowing users to classify flow control keywords different from, say, accessibility or declaration keywords. However, I'm just addressing your particular views on specific flow control keywords, and how they don't seem to match what the langauge is providing.

CyrusNajmabadi commented 7 years ago

Funny, I learned about that in computer classes in school, even before I already read it in books

Could you point out the books that led you to think this was how C# switches work? Note that C# switches follow in the steps of C and the heritage around switches there. In the most common languages htat have these types of switches (C, C++, Java and C#) switches and breaks simply do not really work in this manner.

lachbaer commented 7 years ago

My point was that you appear to have some misconceptions about these C# constructs and how they work. For example, your views on break+switches is not accurate.

Then, again, you got me wrong. I know very well about break and switches! Yes, there are switches without a single break. But that is not the "school version" [see the quotes?] of it. I don't want to lengthen all my comments - they are already too long - that's why I just grab one point from which I think that everybody understand my intention from the context. But obviously I am wrong on that. 😢

Just, please, answer me one - okay, two - (rethorical) question for now: can switch be expressed as a series of consecutive else ifs, possibly or'ed conditions, and default in general (i.e. not always) as a final else? Would you write breaks in there, i.e. breaks for the ifs?

CyrusNajmabadi commented 7 years ago

But that is not the "school version"

I don't know what this means. What is the "school version"? The version you learn in school? If so, it's possible that every individual school teaches things differently (for example, mine covered these concepts).

that's why just grab one point from which I think that everybody understand my intention from the context.

You made a proposal stating that you wanted certain behavior. It's being discussed because it's something you asked for :)

CyrusNajmabadi commented 7 years ago

Just, please, answer me one - okay, two - (rethorical) question

It sounds like you're asking if one flow construct can, generally, be rewritten as another. In which case, i would say "yes, in general, you can replace one flow control construct with another. The clearest case of this is that, effectively, in C# you can avoid nearly all flow constructs and replace them with just "gotos" and "labels" (though the translated version of switch may not be as fast).

It's helpful to even think of constructs like "break/continue" (for loops or switches) as just being specialized "gotos" with implicit labels.

With that, 'switches' then become clear. 'break' (like in a loop) is just a specialized "goto" to a label past the end of the switch (or past the end of the loop ).

--

It's for this reason that i would treat 'break' in a switch the same way i treat 'break' in a loop. It serves the same purpose. It is a means to jump from that point in the construct to the end of the construct.

lachbaer commented 7 years ago

What is the "school version"? The version you learn in school?

Take it as such. Beginners books and tutorials, computer classes, help pages ( and here.

that's why just grab one point from which I think that everybody understand my intention from the context.

You made a proposal stating that you wanted certain behavior. It's being discussed because it's something you asked for :)

Again a language problem? I do not understand how your answer is related to the quote in the slightest way :totally confused!: 😕

CyrusNajmabadi commented 7 years ago

Take it as such. Beginners books and tutorials, computer classes, help pages ( and here.

The help pages you linked to even state:

Execution of the statement list in the selected switch section begins with the first statement and proceeds through the statement list, typically until a jump statement, such as a break, goto case, return, or throw, is reached.

And

but any construct that renders the end point of the statement list unreachable is permitted. For example, a while statement controlled by the Boolean expression true is known to never reach its end point. Likewise, a throw or return statement always transfers control elsewhere and never reaches its end point

--

I do not understand how your answer is related to the quote in the slightest

You stated "breaks in switch statements are expected and belong to the constructs normal appearance". I'm pointing out why i think that statement does not make sense. And any logic that would indicate that was appropriate would apply equally to 'break+continue' in any sort of loop.

lachbaer commented 7 years ago

It sounds like you're asking if one flow construct can, generally, be rewritten as another

I want to clarify that for now and ever. Did you really, really, really think I asked that question because I don't all that already? Though I introduced it with the word "rethorical"?

Please, Cyrus, I really don't want to offend you! 😃 😊 This question is real and friedly ment. Do we both suffer from language difficulties?

miloush commented 7 years ago
switch (a)
{
    break;
    case 0:
        break;
        break;
    case 1;
        if (b)
            break;
        else
            break;
}

Would you highlight any of the above breaks?

CyrusNajmabadi commented 7 years ago

Did you really, really, really think I asked that question because I don't all that already?

I honestly don't know what you know, and i don't want to presume anything. So far, it appears as if you both understand how switches work, but you wish the IDE to pretend that they behave differently. For example, that a 'break' in a switch should be treated specially while 'break' in a for-loop should not be treated specially.

I cannot find any explanation that demonstrates why that should be so.

CyrusNajmabadi commented 7 years ago

@lachbaer At this point it would be good if you would just plainly state what it is you want, and why you think it should behave that way. It's gotten very muddy, and could use some clarification. As this is your proposal, please do me that favor :)

Thanks!

CyrusNajmabadi commented 7 years ago

@miloush Was that intended to be a c# switch, or a c-switch? I ask because the following is not legal C#:

switch (a)
{
    break;
lachbaer commented 7 years ago

I do not understand how your answer is related to the quote in the slightest

You stated [...]

I meant your qoute of mine that you directly put above your comment and that starts with "that's why just grab".

I really think that we both talk of different things. You think that I am a newbie and do not know anything because you undestand wrong what I write. And I think for myself, why does Cyrus answer or write things that I already know in detail.

miloush commented 7 years ago

@CyrusNajmabadi sounds like that one was "unexpected" to you :) anyway, intended, you need to decide whether to mark it, legal or not

CyrusNajmabadi commented 7 years ago

I really that we both talk of different things. You think that I am a newbie and do not know anything because you undestand wrong what I write.

This is not the case. I am simply pointing out that i do not understand the rational you are putting forth for your claims. When i've pointed out that your asks don't seem to match what these constructs are, you have stated you understand that, but you have not provided any logic then why we would still want such a feature to behave how you are asking.

I am asking you plainly to state precisely what it is that you want, and to state plainly why you think it's appropriate for the feature to behave in this manner.

I've given my reasoning why i am ok with the feature, overall, but I don't understand some of the very specific behaviors you've asked for. I've given reasoning based on how the language works, and you've indicated that you're very well aware of how the language works. As such, i'm trying to reconcile the feature-set that you're asking for, given that it doesn't seem to match with how the language works.

CyrusNajmabadi commented 7 years ago

sounds like that one was "unexpected" to you :)

It's more a matter of "that one" simply not being legal C#, and me wanting to know if that was intentional on your part, or not. :)

It could have easily been a mistake on your part. Or an intent to reference C as opposed to C#. I wasn't sure and i just wanted clarification.

--

If you do want this to be C#, then i would say "the IDE will generally handle this as effectively as our parser does. In this case, the parser is not at all resilient to this sort of thing, which you can see here: https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs#L7975

As such, the first "break" won't even be considered to be in a switch statement. The following "case XXX" labels won't even parse properly as we won't even consume 'case' properly unless we're in a switch statement.

miloush commented 7 years ago

@CyrusNajmabadi Alright that missed the point, the intention was to give @lachbaer an opportunity to think about how he actually expects it to work. For the record I am on your side in this case but I thought the discussion is not working well.

@lachbaer Also, sometimes I do just:

public static string ToString(MyEnum e)
{
    switch (e)
    {
        case MyEnum.A: return "A";
        case MyEnum.B: return "B";
        default: return null;
    }
}

(note: this is an illustrative example)

I would argue the return here is as "expected" as breaks in your case.


I think we found the important points though:

lachbaer commented 7 years ago

@CyrusNajmabadi Okay, I will try to explain what I mean in more detail now.

To expain it for switch I must care about loops first. That will be part 1. Switches will be in Part 2.

We should clear our mind and have no preliminary mindset for break. Let's just say, this keyword does not exist for now.

When we have a loop, for, do, while, whatever you like, you assume it to run until its condition is not met anymore. E.g. for foreach its condition is, without going in detail, that the list is empty.

for (int i=10; i > 0 ; --i) {
  // We start at the beginning of the block
  Console.WriteLine(i);
  /* Now we are at the end of the block
     We *expect* the block to end here and
     to start over, to *jump* at the beginning. */
}

In the above sample, we expect the loop to execute 10 times, because its condition says so. The block is executed from top to bottom and no prematurely jumps (jumps to the loop) other as the expected jump at the end of the block to its top again (only when the condition is met of course).

Now take the following loop, with the same conditions.

for (int i=10; i > 0 ; --i) {
  if (i == 9) continue;
  if (i == 5) break;
  Console.WriteLine(i);
}

Without knowing the contents of the for-block we expect the loop to run 10 times. We see the WriteLine(i) and expect 10 lines of output. Now take a piece of paper and overlay the right half of the block.

for (int i=10; i > 0 ; --i) {
  if (i == 9) ...
  if (i == 5) ...
  Console.WriteL ...
}

We see this block and still expect it to run 10 times from top to bottom. We expect the block to jump to the beginnig for 10 times.

But now that break comes:

for (int i=10; i > 0 ; --i) {
  /* ... */
  if (i == 5) break;
  /* ... */
}

Suddenly when reading from top to bottom for the 6th time we can not proceed as expected, but we must jump at the end and out of the block. That was unexpected by the sole signature of for (int i=10; i>0; --i). (Of course it is expected in term of algorithmic logic, because it if condition is met, but that is not the point!)

Absolutely the same counts for `continue.

for (int i=10; i > 0 ; --i) {
  if (i == 9) continue;
  /* ... */
}

The jump to the next iteration of for is unexpected, in the same meaning as it is for break above.

When you read a book (LTR, TTB) you read the pages from top to bottom and the lines from left to right. That's what you expect. When finishing one page, you go to the next page. What you do not expect, thus unexpected, is that in the middle of the page, middle of the line, there stands "continue on next page and ignore the rest of this page".

Now think of loop- and method-blocks (I think you call that statement blocks or so) as that page of a book, from top to bottom. Every finite jump to another section of the page (continue, goto) or to another page (break, return, throw) is unexpected.

My proposal is to highlight these unexpected jump/branch keywords differently from other statements and keywords. Just slightly different. The reason I have already given in other comments.

The next part is about reasoning, why switch-breaks are expected and therefore should not be highlighted.

CyrusNajmabadi commented 7 years ago

Without knowing the contents of the for-block we expect the loop to run 10 times.

That's a very 'unexpected' way of looking at things :)

For example, that's not at all how i look at the signature of a loop. What that signature tells me is that a variable will be initialized to a specific value, when the end of the loop block is reached, some code will execute, and then before entering the loop body a check will be performed.

That is the purpose of the header of the loop and that's what i "expect" when i see it. :)

CyrusNajmabadi commented 7 years ago

My proposal is to highlight these unexpected jump/branch keywords differently from other statements and keywords. Just slightly different. The reason I have already given in other comments.

I have no problem with this.

lachbaer commented 7 years ago

That's a very 'unexpected' way of looking at things :)

I had to break down the argumentation, because I didn't want to write a book. You are again taking me too literally. Please don't do that! seriously :-) I think that in the context of the argumentation it should be absolutely clear.

What you "expect" on the signature is what is going on in our heads, mine too. But that is already too detailed for the purpose of this discussion. Maybe I should have written "What we expect from that specifically declared for loop" is to run 10 times. I thought an educated reader (in the metaphoric sense, not offensive) would imply that.

CyrusNajmabadi commented 7 years ago

Please don't do that! seriously

I'm really lost here. I'm trying to figure out your position. I've even said:

I am asking you plainly to state precisely what it is that you want, and to state plainly why you think it's appropriate for the feature to behave in this manner.

You now have given a response... but don't want me to take your position plainly as you state it...

I think that in the context of the argumentation it should be absolutely clear.

it isn't. that's why i'm asking you for clarification, and why i've asked you several times to just state your position plainly. There is clearly some confusion here. It does not help when you then state a position but tell me to not actually listen to it, but instead infer what your position is.

I'm going to ask again: please "state precisely what it is that you want, and to state plainly why you think it's appropriate for the feature to behave in this manner." :)

Please don't ask me to infer or otherwise guess what your position is. Clearly i haven't been able to do so so far :) And when i try to take our position as what it is stated, that clearly isn't helping either. Consider it your job to make this make sense to me in a simple and clear manner. Tnx :)

CyrusNajmabadi commented 7 years ago

Please, Cyrus, I really don't want to offend you!

I'm not at all offended. I simply want clarity on what your position is. Clearly there is a disconnect. I leave it up to you to provide more info here to help clear this all up. Thanks!

lachbaer commented 7 years ago

Part II

Now let's take a switch statement from the C# Reference

int caseSwitch = 1;
switch (caseSwitch)
{
    case 1:
        Console.WriteLine("Case 1");
        break;
    case 2:
        Console.WriteLine("Case 2");
        break;
    default:
        Console.WriteLine("Default case");
        break;
}

First let's rewrite this as an if structure:

if (caseSwitch == 1)
    Console.WriteLine("Case 1");
else if (caseSwitch == 2)
    Console.WriteLine("Case 2");
else
    Console.WriteLine("Default clase");

Interistingly there are no more breaks anymore. You read it from top to bottom, the whole construct taken for granted as it is since centuries.

If you read the first if (caseSwitch == 1) you expect to skip the directly following (block) statement and proceed with the next else if or else until any condition is met or the end is reached. There is no difference from for (int i = 10; i>=; i--) from the previous part, where you expect the loop block or statement to be executed or not based on the condition, like with our if here.

Given a switch statement again:

...
    case 1:
        break;
    case 2:
...

I see that "after the case 1 condition if have to look at the case 2 condition in case it is not fulfilled". That break; is like a }. I expect an "end of the block token".

Let me rewrite the upgraded switch statement in a C# derived pseudo-code:

switch (caseSwitch)
{
    case 1 {
        Console.WriteLine("Case 1");
    }
    case 2    // this is or'ed to the next case, because it has no statement, nor block
    case 3 {
        Console.WriteLine("Case 2");
    }
    default {
        Console.WriteLine("Default case");
     }
}

You will see the similarity to if statements quite clearly now. I expect the cases to be top-down like the corresponding if was. There are no unexpected breakouts from the blocks in unexpected positions.

As I said in Part 1 above, clear your mind from the word break and make it a semantic "block-ends" token. Don't dive too deep into the theoretical analysis what on IL level jumps are.

Taken the example from another comment above

switch (e)
{
    case MyEnum.A: return "A";
    case MyEnum.B: return "B";
    default: return null;
}

And rewritten to pseude-code:

switch (e)
{
    case MyEnum.A {
        return "A";
    }
    case MyEnum.B {
        return "B";
    }
    default {
        return null;
    }
}

As you can see, the return statements do not only leave the switch statement but also the enclosing blocks down to the method block level.

The switch is not processed as previously expected down until the end, namely the closing } of the switch itself.

Imagine there at the end is a virtual sequence point (no matter now if there really is one or not). Under normal circumstances that would have been reached. But because of the premature, and thus unexpected return it is not.

I hope I made clear, what I mean with expected and unexpected branches and break specifically.

Those switch-breaks could be rewritten to be ommited in pseudo-code, but the other ("unexpected branch") statements cannot.

Conclusion

breaks in switch statements, though technically branching, don't have the same contentual meaning as loop-breaking breaks. It is just the same keyword, a homonym. But in this meaning it is not an unexpected, as explained and declared above, break.

When parsing you could create an unique token for each of those meanings. That would facilitate distinction between those two. It would actually make sense, when looked upon it the way described here.

CyrusNajmabadi commented 7 years ago

When parsing you could create an unique token for each of those meanings.

We could have. But we didn't. Because, like the rest of the C family, there is no reason to. The meaning is the same. It is an unconditional jump to the end of the construct.

I hope I made clear, what I mean with expected and unexpected

Yes. That is clear. I don't happen to agree with it. But i can see clearly how you are thinking about these constructs.

Here's a simple example:

As you can see, the return statements do not only leave the switch statement but also the enclosing blocks down to the method block level.

Sure... but it could also have a 'continue'. Or a 'goto'. or any sort of construct that prevents fallthrough from the switch statement.

I think you've internalized 'break' as being special. When, in practice, it's simply common. But just as it's common for switches, 'break' is also common for loops. It's the way to get to the end of hte construct, and it's often used because we do not allow fallthrough.

If you want to specialize it for it's commonality, then that's fine. But then i would also specialize it for its commonality in a loop.

--

Note: i understand your beliefs around 'expected/unexpected', i just don't share in them at all. For me to have any sort fo feeling toward 'expected/unexpected', then it would have to vastly represent the majority of all code out there. At that point i can start 'expecting' things, and i can see other things an 'unexpected'. However, here, that's simply not what's going on. The expectation for all loops is that they may terminate early for any number of reasons. And the requirement for all switch sections is that if they have statements, their end point can not be reachable. That's commonly done with 'breaks' but that's not my expectation at all.

lachbaer commented 7 years ago

Consider it your job to make this make sense to me in a simple and clear manner.

I think we have massive cultural background differences here, language and expression differences maybe. I used to explain a lot to people in my profession and still do sometime. I frequently get compliments for explaining so clearly - you probably cannot believe that now 😆

Due to that I think that could not be helped. If you do not understand me now after my comprehensive practical explanations, I'm lost.

And I kindly suggest that you, too, try to understand why you did not understand me. Maybe you'll find a clue somewhere sometime 😃

CyrusNajmabadi commented 7 years ago

If you do not understand me now after my comprehensive practical explanations

I think i understand waht you're asking for. And i think i understand why you want them. I simply do not agree with the set of steps you've taken by which you've internalized and see the language now.

--

Note: as before, i see value in allowing flow control statement keywords to be colored differently than other keywords. But your basis and logic for how 'break' works in a switch doesn't follow for me, and i would simply classify 'breaks' in a switch as i would classify "return/goto/continue/throw" in a switch. They are the places where control flow operations are specified by the user in the switch. 'break' has no reason to be treated specially as it isn't actually special :)

CyrusNajmabadi commented 7 years ago

And I kindly suggest that you, too, try to understand why you did not understand me

A large part of why i did not understand you was that i took what you were saying literally. As you have yourself mentioned, i should not have been doing that, and i should have been inferring what you meant, and just making assumptions about what i thought you meant.

I personally don't like doing that. Once i start having to make assumptions then everything can go off the rails. I could assume any number of things. I would much rather just have you lay our your argument clearly and plainly. I can then decide what i think of it. For example, now that you've laid out your argument, i can see the reasons clearly why i disagree with it.

CyrusNajmabadi commented 7 years ago

When parsing you could create an unique token for each of those meanings.

This is likely true. Though given their exact same meaning, it would have been very redundant. But let's do the thought exercise and presume we had a switchbreak; statement. If we did that, we would still classify those keywords as they served the same purpose of construct-flow-control just like the others did. Just as we would classify forcontinue the same as whilecontinue and so on and so forth. Each of these constructs could have had unique keywords. But, in the end, they're all just about appropriate flow control, and treating switches so differently from the rest is just not something we'd do.

That's why, for example, we don't bother working with the other tokens that make up a switch substantially different than the other constructs in the language. We could, after all. For example, in a switch, the {} is required, while there is no such requirement for loops and whatnot. But we don't, because we view them as all being in the same family which should basically be treated very similarly to each other.

lachbaer commented 7 years ago

The meaning is the same

The technical meaning, not the contextual (or semantical [spoken language}).

It is an unconditional jump to the end of the construct.

Then why don't if blocks have a break; at the end? - this again is a rethorical question.

But i can see clearly how you are thinking about these constructs.

"Thinking" is the wrong word for this, as I understand that word. It is a "view on things", a philosophy rather than a thinking. And like with all other philosophies, we can endlessly argue about it. That's the price of an "open minded" world 😉

Sure... but it could also have a 'continue'. Or a 'goto'. or any sort of construct that prevents fallthrough from the switch statement.

I didn't want to mention them all. return just describes the point the best. The control does not "flow through" to the end, it is jump to or over or away or whatsoever. (Besides I really very well know how all those constructs work internally. I did assembler in my early days also.)

I think you've internalized 'break' as being special

Then still you don't get my point. break is a language teapot, a homonym. Internally and technically, yes, it jumps to the end of the construct. Because C (and maybe earlier languages, too) is so near to the machine language, from that technical point of view it makes sense. But from the philosophic point of view I took to explain what I mean it is completely different. Ask your broader team, I bet that somebody agrees with me.

'break' has no reason to be treated specially as it isn't actually special

And that is the point where we diverge. Again, technically I agree.

Maybe one more point

label:
foreach (int item in list) {
    switch (item) {
        case 0: break;
        case 1: return;
        case 2: continue;
        case 3: throw new Exception();
        case 4: goto label;
     }
}

What is the difference between case 0 and the others? All 'branching' statements influence the outer (foreach) block, except for break. Imagine break would influence the foreach and a further keyword exitswitch existed. Can you see that highlighting the classical branching keywords has more meaning than highlighting the exitswitch keyword, that acutally does what you would expect the switch statement to do? Namely continuing in the flow.

CyrusNajmabadi commented 7 years ago

Can you see that highlighting the classical branching keywords has more meaning

I can see how you see it that way. :)

I don't personally see it that way, nor do i think we would codify that sort of interpretation in the IDE.

And like with all other philosophies, we can endlessly argue about it.

We can, but I'm not sure there's much value doing that. I see your perspective but i don't think it would be worth the effort to try to implement that if we were going to this overall feature.

Are there any other cases you think are special and should be done differently?

lachbaer commented 7 years ago

Just as we would classify forcontinue the same as whilecontinue and so on and so forth.

I understand your view on the things, and me too, I would never ever distinguish between forcontinue and whilecontinue. That goes way too far!!!

This is likely true. Though given their exact same meaning

Here, again, I disagree. I think I made myself clear why the are differnent. Of course they have similarities, but they are different - and again, not technically. Jumping is jumping, whether for fun or for work 😉

There is one scenario where I would agree that it has this "premature exiting" character (that expression should be clear, exiting loops or methods to be precise):

foreach (int item in list) {
    switch (item) {
        case 0:
            DoSomethingWithItem(ref item);
            if (item < 0)
                break;
            ProceedWithCase0Processing(item);
            break;
...

Here the first break is a premature escape from the case. But the second just serves the purpose of a closing } , in meaning.

CyrusNajmabadi commented 7 years ago

But the second just serves the purpose of a closing } , in meaning.

We disagree. And it's highly unlikely we would take such an interpretation in this sort of feature.

Note that this really isn't the meaning of this "break" at all. For example, this code would not be legal:

switch (item) {
    case 0: {
         DoStuff();
    } 
}

even though there is a } that ends that switch section, it is not legal code. Because the code 'falls through'. As such, the end of the switch section must not be reachable. That's the purpose of the 'break', to make the end unreachable, and to do so by unconditionally branching somewhere else. It is definitely not to act as some sort of close curly. Note that in the above code you'd have to write either:

switch (item) {
    case 0: {
         DoStuff();
         break;  // this is not premature, or post mature, or anything.  it just ensures that the end of the switch is not reachable.
    } 
}

or

switch (item) {
    case 0: {
         DoStuff();
    } 
    break;
}

Both would be fine, but some approach would be necessary precisely because a normal block's endpoint is reachable, which means the switch section's end point is reachable, which is precisely what is disallowed in a switch.

lachbaer commented 7 years ago

Are there any other cases you think are special and should be done differently?

Indeed, while discussing with you. 😺

What do you, hypothetically, think about classifying only switch breaks differently, that serve ending the case block only? case with statements must leave the switch, with break being the most common case. In this situation it simpy has no other meanig than } of an if block has (see below).

You can classify loop branching and goto statements in one category, method exiting statements in a second one.

But the second just serves the purpose of a closing } , in meaning.

We disagree.

What other purpose does it serve? It jumps to the end of the switch, doesn't it? What does a closing block of an if or else if do? It jumps the the end of the whole if construct (I know they are actually all single 'if's just concatenated). See => same purpose.