Roslyn: IDE work items for c# 9.0 patterns.

Created on 12 Mar 2020  路  14Comments  路  Source: dotnet/roslyn

Language/Compiler work: https://github.com/dotnet/csharplang/issues/2850~~ https://github.com/dotnet/csharplang/pull/3361

These are the things we will need to support and/or validate:

  • [x] [Cyrus] Update features that do null checks do is not null. https://github.com/dotnet/roslyn/pull/43322
  • [x] [Cyrus] Update existing pattern analyzers to try to use and, or, not patterns when appropriate. https://github.com/dotnet/roslyn/pull/43324
  • [X] we need SymbolInfo/TypeInfo. This will ensure that quick-info goto/def/find-refs works. (See https://github.com/dotnet/roslyn/pull/42357)
  • [x] [Sam]Update fixer that adds discards to not patterns anymore (if the lang version is correct). #43528, #43540
  • [x] [Sam]Automatic Brace Completion needs to work for pattern parens. #43522
  • [x] [Compilers, Tomas] Properly classify debugger breakpoint spans for subexpressions. (good to go since https://github.com/dotnet/roslyn/pull/42711 and https://github.com/dotnet/roslyn/pull/43154)
  • [X] [Compilers] Switch expression stepping not working correctly: https://github.com/dotnet/roslyn/issues/43468 (https://github.com/dotnet/roslyn/pull/43471)
  • [x] [Compilers] Check Interactive window (https://github.com/dotnet/roslyn/pull/43358)
  • [x] [Tomas] Test Active Statement tracking in switch expression arms (part of EnC, editing itself is still unsupported: https://github.com/dotnet/roslyn/issues/43099).
  • [X] [Compilers] Check with @gafter for the set of new diagnostics reported. (See https://github.com/dotnet/roslyn/issues/42368#issuecomment-613704215)
  • [X] [Compilers] Make sure we don't crash on unreachable case diagnostics. (Added code to prevent the compiler crashing when this diagnostic is given. Joke. See https://github.com/dotnet/roslyn/pull/43280)
  • [X] [Compiler] Ensure IOp works. (See https://github.com/dotnet/roslyn/pull/42474, https://github.com/dotnet/roslyn/pull/42548)
  • [x] [Allison] Completion. We need it for not, and, or. Need to make sure we're at soft selecting in places where new comparison patterns are allowed so we don't interfere with typing. #43365, #43574
  • [X] [Debugger] Disambiguate sequence points with equal text span: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1106749.
  • [x] [Cyrus] Add feature to convert from !(x is null) to x is not null. https://github.com/dotnet/roslyn/pull/43438
  • [x] [Cyrus] Update parentheses simplification and addition. https://github.com/dotnet/roslyn/pull/43438
  • [x] [Cyrus] Complexify/simplify. https://github.com/dotnet/roslyn/pull/43438
  • [x] [Cyrus] update invert-if to invert patterns. https://github.com/dotnet/roslyn/pull/43438
  • [x] [Sam] Formatting has to be updated to understand these constructs entirely. We have lots of options around whitespace around operators that will/won't apply when these are in patterns. e.g. (>= #43538
  • [x] [David] Indentation. https://github.com/dotnet/roslyn/pull/43606
  • [ ] Add feature to offer to use not null.
  • [ ] Need to ensure that features that select subexpressions don't work on subpatterns (i.e. can't extract a portion of a pattern).
  • [ ] [Sam][LowPriority]case string _ =>. Add analzyer that removes unnecessary _ .
  • [ ] [Sam][Need Validation] Features based on unbound types. i.e. Add Using, Generate type.
  • [ ] Add missing cases.
  • [ ] [Cyrus] Validate TypeInferrer
  • [ ] [Sam] split/merge ifs (likely do not support anything here)
  • [ ] we would take a refactoring to convert to/from if-chains to and/or patterns
  • [ ] would take a refactoring to convert long chains of comparisons to patterns.
Area-IDE Concept-Continuous Improvement

All 14 comments

cc @mikadumont for info

we would take a refactoring to convert to/from if-chains to and/or patterns
would take a refactoring to convert long chains of comparisons to patterns.

Some other ideas for refactorings:

  • [ ] Convert subsequent switch case labels to or patterns
    case p: case q: -> case p or q:
  • [ ] Convert subsequent switch case labels to relational+and patterns
    case 'a': .. case 'z': -> case >= 'a' and <= 'z':

These might be useful to simplify current workarounds:

  • [ ] Convert switch expressions to is+or patterns if applicable i.e. all arm values are boolean constants
  • [ ] Use or-patterns in switch expression arms if applicable i.e. there's some identical arm values

Some of these might overlap though, and we should decide which one should be an analyzer versus a refactoring.

case string _ =>. Add analzyer that removes unnecessary _ .

I think this should be in the simplifier. There are other cases that need to be covered there.

Update existing pattern analyzers to try to use and, or, not patterns when appropriate

This should include if-to-switch and switch-statement-to-expression in particular, those are specifically limited due to lack of or-patterns.

Worth to consider:

  • fade out default: when the switch is already exhaustive.
  • fix overlapping/redundant relational patterns if the compiler doesn't complain e.g. =>2 and =<4 or =>3 and =<5

great stuff. Thanks @alrz !

New diagnostics for pattern-matching in C# 9.0

CS8780 ERR_DesignatorBeneathPatternCombinator

You aren't permitted to declare a pattern variable under an or or not
pattern combinator, so we produce this error when you attempt to do so.

  if (o is int or long l)        // error: A variable may not be declared within a 'not' or 'or' pattern.
  if (o is Bar { F: not int i }) // error: A variable may not be declared within a 'not' or 'or' pattern.

CS8781 ERR_UnsupportedTypeForRelationalPattern

The new relational patterns work only for the built-in types that
have comparison operator. If you attempt to use them for other
types it is an error

  string s = ...;
  if (s < "aardvark") // error: Relational patterns may not be used for a value of type 'string'.

CS8782 ERR_RelationalPatternWithNaN

The new relational patterns do not permit comparing with NaN.

  double d = ...;
  switch (d)
  {
      case < double.NaN: // error: Relational patterns may not be used for a floating-point NaN.

CS8793 WRN_GivenExpressionAlwaysMatchesPattern

When a new pattern form always matches in an is-pattern expression because the input is a constant, we warn.

  if (1 is < 10) // warning: The given expression always matches the provided pattern.

CS8794 WRN_IsPatternAlways

When a new pattern form always matches in an is-pattern (whether or not the input is a constant), we warn:

  int x = ...;
  if (x is <= int.MaxValue) // warning: The input always matches the provided pattern.

Active statement tracking in switch expression arms works well.
Found a couple of related issues:
[Debugger] Disambiguate sequence points with equal text span: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1106749.
[Compilers] Switch expression stepping not working correctly: #43468

[Sam][LowPriority] case string _ =>. Add analzyer that removes unnecessary _ .

@sharwell I can take this if you're not working on it.

@CyrusNajmabadi

A langversion-dependant reducer works here without a need for an analyzer, right?
(much like CSharpDefaultExpressionReducer)

@alrz Sure go ahead! Also consider the following case, which is not new for C# 9 but would benefit from the same simplification:

if (o is string _)

Closing out. Any remaining work/enhancements can come through directed issues.

@gafter

You aren't permitted to declare a pattern variable under an or or not
pattern combinator

Can you point me to more documentation on this limitation?

I just stumbled upon it on a scenario where it didn't make a lot of sense to me:

if (x is { Code: not 1 code })
{
    return new Error(code);
}

The value 1 above is a "success" indicator in my domain, and I want to match "when the code is not 1" then generate an error with the actual code in it.

Can you elaborate why this is an invalid scenario? The restriction you mentioned doesn't seem to make sense when using constant values on the match IMHO.

Seems like the only way out of this is degrading the code into using a when like:

if (x is { Code: var code } when code != 1)
{
    return new Error(code);
}

FAKE EDIT:

Found a really weird (undocumented?) way of extracting the value, by repeating the same property twice:

if (x is { Code: not 1, Code: var code })
{
    return new Error(code);
}

Can you elaborate why this is an invalid scenario?

Because nothing in the language allows it. Neither hte 'not' nor 'constant' patterns allow you to name the result. We'd need a proposal allowing that for that to work :)

Found a really weird (undocumented?) way of extracting the value, by repeating the same property twice:

Now that is weird. @333fred @jcouv is that supposed to be allowed? Seems really really strange and unintentional.

Huh. I agree it's odd, but I don't see anything in the spec that would disallow this: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/patterns.md#property-pattern.

@julealgon Try:

if (x is { Code: not 1 and var code })
{
    return new Error(code);
}

Now that is weird. ... Seems really really strange and unintentional.

I agree @CyrusNajmabadi . I honestly thought it would give me some sort of error at runtime the first time I tried it, or match the values incorrectly.

@julealgon Try:

if (x is { Code: not 1 and var code })
{
   return new Error(code);
}

It works @alrz ! Interesting... is that how it is supposed to be done at the end of the day? I don't think I've ever seen an example like that.

is that how it is supposed to be done at the end of the day?

Yes. This is definitely a supported and used pattern today. In the future, we might consider a way of making this more terse. But absent that, the above is def a realistic way to go.

Was this page helpful?
0 / 5 - 0 ratings