Skip to content

Commit 112c9a7

Browse files
committed
Project setting rude edits
1 parent ead031c commit 112c9a7

35 files changed

+843
-108
lines changed

src/Compilers/CSharp/Portable/CSharpParseOptions.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,15 @@ protected override ParseOptions CommonWithFeatures(IEnumerable<KeyValuePair<stri
162162
/// </summary>
163163
public new CSharpParseOptions WithFeatures(IEnumerable<KeyValuePair<string, string>>? features)
164164
{
165-
ImmutableDictionary<string, string> dictionary =
166-
features?.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase)
167-
?? ImmutableDictionary<string, string>.Empty;
165+
if (Features == features)
166+
{
167+
return this;
168+
}
169+
170+
if (features is not ImmutableDictionary<string, string> dictionary || dictionary.KeyComparer != StringComparer.OrdinalIgnoreCase)
171+
{
172+
dictionary = (features ?? []).ToImmutableDictionary(StringComparer.OrdinalIgnoreCase);
173+
}
168174

169175
return new CSharpParseOptions(this) { _features = dictionary };
170176
}

src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,23 @@ public void WithXxx()
4545
TestProperty((old, value) => old.WithLanguageVersion(value), opt => opt.LanguageVersion, LanguageVersion.CSharp3);
4646
TestProperty((old, value) => old.WithDocumentationMode(value), opt => opt.DocumentationMode, DocumentationMode.None);
4747
TestProperty((old, value) => old.WithPreprocessorSymbols(value), opt => opt.PreprocessorSymbols, ImmutableArray.Create<string>("A", "B", "C"));
48+
}
49+
50+
[Fact]
51+
public void WithPreprocessorSymbols()
52+
{
53+
Assert.Equal(0, CSharpParseOptions.Default.WithPreprocessorSymbols(ImmutableArray.Create("A", "B")).WithPreprocessorSymbols(default(ImmutableArray<string>)).PreprocessorSymbols.Length);
54+
Assert.Equal(0, CSharpParseOptions.Default.WithPreprocessorSymbols(ImmutableArray.Create("A", "B")).WithPreprocessorSymbols((IEnumerable<string>)null).PreprocessorSymbols.Length);
55+
Assert.Equal(0, CSharpParseOptions.Default.WithPreprocessorSymbols(ImmutableArray.Create("A", "B")).WithPreprocessorSymbols((string[])null).PreprocessorSymbols.Length);
56+
}
4857

49-
Assert.Equal(0, CSharpParseOptions.Default.WithPreprocessorSymbols(ImmutableArray.Create<string>("A", "B")).WithPreprocessorSymbols(default(ImmutableArray<string>)).PreprocessorSymbols.Length);
50-
Assert.Equal(0, CSharpParseOptions.Default.WithPreprocessorSymbols(ImmutableArray.Create<string>("A", "B")).WithPreprocessorSymbols((IEnumerable<string>)null).PreprocessorSymbols.Length);
51-
Assert.Equal(0, CSharpParseOptions.Default.WithPreprocessorSymbols(ImmutableArray.Create<string>("A", "B")).WithPreprocessorSymbols((string[])null).PreprocessorSymbols.Length);
58+
[Fact]
59+
public void WithFeatures()
60+
{
61+
var options1 = CSharpParseOptions.Default.WithFeatures(new Dictionary<string, string>() { { "F1", "V1" }, { "F2", "V2" } });
62+
var options2 = CSharpParseOptions.Default.WithFeatures(new Dictionary<string, string>() { { "f2", "V2" }, { "F1", "V1" } });
63+
64+
Assert.True(options1.Equals(options2));
5265
}
5366

5467
/// <summary>

src/Compilers/Core/Portable/Compilation/ParseOptions.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ protected bool EqualsHelper([NotNullWhen(true)] ParseOptions? other)
141141
return
142142
this.SpecifiedKind == other.SpecifiedKind &&
143143
this.DocumentationMode == other.DocumentationMode &&
144-
this.Features.SequenceEqual(other.Features) &&
144+
FeaturesEqual(Features, other.Features) &&
145145
(this.PreprocessorSymbolNames == null ? other.PreprocessorSymbolNames == null : this.PreprocessorSymbolNames.SequenceEqual(other.PreprocessorSymbolNames, StringComparer.Ordinal));
146146
}
147147

@@ -156,6 +156,37 @@ protected int GetHashCodeHelper()
156156
Hash.Combine(Hash.CombineValues(this.PreprocessorSymbolNames, StringComparer.Ordinal), 0))));
157157
}
158158

159+
private static bool FeaturesEqual(IReadOnlyDictionary<string, string> features, IReadOnlyDictionary<string, string> other)
160+
{
161+
if (ReferenceEquals(features, other))
162+
{
163+
return true;
164+
}
165+
166+
if (features.Count != other.Count)
167+
{
168+
return false;
169+
}
170+
171+
foreach (var (key, value) in features)
172+
{
173+
if (!other.TryGetValue(key, out var otherValue) || value != otherValue)
174+
{
175+
return false;
176+
}
177+
}
178+
179+
foreach (var (key, _) in other)
180+
{
181+
if (!features.ContainsKey(key))
182+
{
183+
return false;
184+
}
185+
}
186+
187+
return true;
188+
}
189+
159190
private static int HashFeatures(IReadOnlyDictionary<string, string> features)
160191
{
161192
int value = 0;

src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3074,4 +3074,29 @@ private static bool DeclareSameIdentifiers(SyntaxToken[] oldVariables, SyntaxTok
30743074
}
30753075

30763076
#endregion
3077+
3078+
protected override IEnumerable<Diagnostic> GetParseOptionsRudeEdits(ParseOptions oldOptions, ParseOptions newOptions)
3079+
{
3080+
foreach (var rudeEdit in base.GetParseOptionsRudeEdits(oldOptions, newOptions))
3081+
{
3082+
yield return rudeEdit;
3083+
}
3084+
3085+
var oldCSharpOptions = (CSharpParseOptions)oldOptions;
3086+
var newCSharpOptions = (CSharpParseOptions)newOptions;
3087+
3088+
if (oldCSharpOptions.LanguageVersion != newCSharpOptions.LanguageVersion)
3089+
{
3090+
yield return CreateProjectRudeEdit(ProjectSettingKind.LangVersion,
3091+
oldCSharpOptions.SpecifiedLanguageVersion.ToDisplayString(),
3092+
newCSharpOptions.SpecifiedLanguageVersion.ToDisplayString());
3093+
}
3094+
3095+
if (oldCSharpOptions.PreprocessorSymbolNames.SequenceEqual(newCSharpOptions.PreprocessorSymbolNames, StringComparer.Ordinal))
3096+
{
3097+
yield return CreateProjectRudeEdit(ProjectSettingKind.DefineConstants,
3098+
string.Join(",", oldCSharpOptions.PreprocessorSymbolNames),
3099+
string.Join(",", newCSharpOptions.PreprocessorSymbolNames));
3100+
}
3101+
}
30773102
}

src/Features/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,9 @@ public static void Main()
370370
var result = await AnalyzeDocumentAsync(oldProject, newSolution.GetDocument(documentId));
371371

372372
Assert.True(result.HasChanges);
373-
Assert.True(result.HasChangesAndErrors);
374-
Assert.True(result.HasChangesAndSyntaxErrors);
373+
Assert.True(result.AnalysisBlocked);
374+
Assert.NotNull(result.SyntaxError);
375+
Assert.False(result.HasBlockingRudeEdits);
375376
}
376377

377378
[Fact]
@@ -395,8 +396,9 @@ public static void Main()
395396
var result = await AnalyzeDocumentAsync(oldProject, oldDocument);
396397

397398
Assert.False(result.HasChanges);
398-
Assert.False(result.HasChangesAndErrors);
399-
Assert.False(result.HasChangesAndSyntaxErrors);
399+
Assert.True(result.AnalysisBlocked);
400+
Assert.NotNull(result.SyntaxError);
401+
Assert.False(result.HasBlockingRudeEdits);
400402
}
401403

402404
[Fact]
@@ -432,8 +434,9 @@ public static void Main()
432434
var result = await AnalyzeDocumentAsync(oldProject, newSolution.GetDocument(documentId));
433435

434436
Assert.False(result.HasChanges);
435-
Assert.False(result.HasChangesAndErrors);
436-
Assert.False(result.HasChangesAndSyntaxErrors);
437+
Assert.True(result.AnalysisBlocked);
438+
Assert.NotNull(result.SyntaxError);
439+
Assert.False(result.HasBlockingRudeEdits);
437440
}
438441

439442
[Fact]
@@ -464,8 +467,9 @@ public static void Main()
464467
var result = await AnalyzeDocumentAsync(oldProject, oldDocument);
465468

466469
Assert.False(result.HasChanges);
467-
Assert.False(result.HasChangesAndErrors);
468-
Assert.False(result.HasChangesAndSyntaxErrors);
470+
Assert.False(result.AnalysisBlocked);
471+
Assert.False(result.HasBlockingRudeEdits);
472+
Assert.Null(result.SyntaxError);
469473
Assert.True(result.RudeEdits.IsEmpty);
470474
}
471475

@@ -512,8 +516,8 @@ public static void Main()
512516
var result = await AnalyzeDocumentAsync(oldProject, newSolution.GetDocument(documentId));
513517

514518
Assert.True(result.HasChanges);
515-
Assert.True(result.HasChangesAndErrors);
516-
Assert.False(result.HasChangesAndSyntaxErrors);
519+
Assert.True(result.AnalysisBlocked);
520+
Assert.False(result.HasBlockingRudeEdits);
517521
Assert.Equal(RudeEditKind.ExperimentalFeaturesEnabled, result.RudeEdits.Single().Kind);
518522
}
519523
}
@@ -542,8 +546,9 @@ public static void Main()
542546
var result = await AnalyzeDocumentAsync(oldProject, oldDocument);
543547

544548
Assert.False(result.HasChanges);
545-
Assert.False(result.HasChangesAndErrors);
546-
Assert.False(result.HasChangesAndSyntaxErrors);
549+
Assert.False(result.HasBlockingRudeEdits);
550+
Assert.False(result.AnalysisBlocked);
551+
Assert.Null(result.SyntaxError);
547552
}
548553

549554
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/10683")]
@@ -583,8 +588,9 @@ public static void Main()
583588
Assert.True(result.HasChanges);
584589

585590
// no declaration errors (error in method body is only reported when emitting):
586-
Assert.False(result.HasChangesAndErrors);
587-
Assert.False(result.HasChangesAndSyntaxErrors);
591+
Assert.False(result.HasBlockingRudeEdits);
592+
Assert.False(result.AnalysisBlocked);
593+
Assert.Null(result.SyntaxError);
588594
}
589595

590596
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/10683")]
@@ -623,8 +629,8 @@ public static void Main(Bar x)
623629

624630
// No errors reported: EnC analyzer is resilient against semantic errors.
625631
// They will be reported by 1) compiler diagnostic analyzer 2) when emitting delta - if still present.
626-
Assert.False(result.HasChangesAndErrors);
627-
Assert.False(result.HasChangesAndSyntaxErrors);
632+
Assert.False(result.AnalysisBlocked);
633+
Assert.False(result.HasBlockingRudeEdits);
628634
}
629635

630636
[Fact]

0 commit comments

Comments
 (0)