Skip to content

Commit ff59bca

Browse files
Enables Nullable Reference Types
Enables Nullable Reference Types throughout the project. Adds null checks and throws exceptions where syntax root or semantic model retrieval fails, improving code safety and preventing potential null reference exceptions.
1 parent e888619 commit ff59bca

File tree

15 files changed

+100
-38
lines changed

15 files changed

+100
-38
lines changed

Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
<PropertyGroup>
33
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
44
<LangVersion>14.0</LangVersion>
5+
<Nullable>enable</Nullable>
56
</PropertyGroup>
67
</Project>

IntelliTect.Analyzer/IntelliTect.Analyzer.CodeFixes/AsyncVoid.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,21 @@ public sealed override FixAllProvider GetFixAllProvider()
2626

2727
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
2828
{
29-
SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
29+
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
30+
if (root is null)
31+
{
32+
return;
33+
}
3034

3135
Diagnostic diagnostic = context.Diagnostics.First();
3236
Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;
3337

3438
// Find the type declaration identified by the diagnostic.
3539
var declaration = root.FindToken(diagnosticSpan.Start).Parent as MethodDeclarationSyntax;
40+
if (declaration is null)
41+
{
42+
return;
43+
}
3644

3745
// Register a code action that will invoke the fix.
3846
context.RegisterCodeFix(
@@ -45,7 +53,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
4553

4654
private static async Task<Document> MakeReturnTask(Document document, MethodDeclarationSyntax declaration, CancellationToken cancellationToken)
4755
{
48-
SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
56+
SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)
57+
?? throw new System.InvalidOperationException("Could not get syntax root");
4958
SyntaxNode newRoot = root.ReplaceNode(declaration.ReturnType, SyntaxFactory.ParseTypeName(typeof(Task).Name).WithTrailingTrivia(SyntaxFactory.Space));
5059
return document.WithSyntaxRoot(newRoot);
5160
}

IntelliTect.Analyzer/IntelliTect.Analyzer.CodeFixes/AttributesOnSeparateLines.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ public sealed override FixAllProvider GetFixAllProvider()
2929

3030
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
3131
{
32-
SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
32+
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
33+
if (root is null)
34+
{
35+
return;
36+
}
3337

3438
Diagnostic diagnostic = context.Diagnostics.First();
3539
Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;
@@ -38,14 +42,14 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
3842
SyntaxToken declaration = root.FindToken(diagnosticSpan.Start);
3943

4044
// Find the enclosing AttributeList
41-
SyntaxNode attributeList = declaration.Parent;
45+
SyntaxNode attributeList = declaration.Parent!;
4246
while (!attributeList.IsKind(SyntaxKind.AttributeList))
4347
{
44-
attributeList = attributeList.Parent;
48+
attributeList = attributeList.Parent!;
4549
}
4650

4751
// Get the class, method or property adjacent to the AttributeList
48-
SyntaxNode parentDeclaration = attributeList.Parent;
52+
SyntaxNode parentDeclaration = attributeList.Parent!;
4953

5054
// Register a code action that will invoke the fix.
5155
context.RegisterCodeFix(
@@ -78,7 +82,8 @@ private static async Task<Document> PutOnSeparateLine(Document document, SyntaxN
7882
.WithAdditionalAnnotations(Formatter.Annotation);
7983

8084
// Replace the old local declaration with the new local declaration.
81-
SyntaxNode oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
85+
SyntaxNode oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)
86+
?? throw new InvalidOperationException("Could not get syntax root");
8287
SyntaxNode newRoot = oldRoot.ReplaceNode(parentDeclaration, newNode);
8388

8489
return document.WithSyntaxRoot(newRoot);

IntelliTect.Analyzer/IntelliTect.Analyzer.CodeFixes/NamingFieldPascalUnderscore.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ public sealed override FixAllProvider GetFixAllProvider()
2727

2828
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
2929
{
30-
SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
30+
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
31+
if (root is null)
32+
{
33+
return;
34+
}
3135

3236
Diagnostic diagnostic = context.Diagnostics.First();
3337
TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;
@@ -50,8 +54,16 @@ private static async Task<Solution> MakePascalWithUnderscore(Document document,
5054
string nameWithoutUnderscore = nameOfField.TrimStart('_');
5155
string newName = "_" + char.ToUpper(nameWithoutUnderscore.First()) + nameWithoutUnderscore.Substring(1);
5256

53-
SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
54-
ISymbol symbol = semanticModel.GetDeclaredSymbol(declaration.Parent, cancellationToken);
57+
SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
58+
if (semanticModel is null || declaration.Parent is null)
59+
{
60+
return document.Project.Solution;
61+
}
62+
ISymbol? symbol = semanticModel.GetDeclaredSymbol(declaration.Parent, cancellationToken);
63+
if (symbol is null)
64+
{
65+
return document.Project.Solution;
66+
}
5567
Solution solution = document.Project.Solution;
5668
SymbolRenameOptions options = new()
5769
{

IntelliTect.Analyzer/IntelliTect.Analyzer.CodeFixes/NamingIdentifierPascal.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ public sealed override FixAllProvider GetFixAllProvider()
2727

2828
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
2929
{
30-
SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
30+
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
31+
if (root is null)
32+
{
33+
return;
34+
}
3135

3236
Diagnostic diagnostic = context.Diagnostics.First();
3337
TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;
@@ -49,8 +53,16 @@ private static async Task<Solution> MakePascal(Document document, SyntaxToken de
4953
string nameOfField = declaration.ValueText;
5054
string newName = char.ToUpper(nameOfField.First()) + nameOfField.Substring(1);
5155

52-
SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
53-
ISymbol symbol = semanticModel.GetDeclaredSymbol(declaration.Parent, cancellationToken);
56+
SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
57+
if (semanticModel is null || declaration.Parent is null)
58+
{
59+
return document.Project.Solution;
60+
}
61+
ISymbol? symbol = semanticModel.GetDeclaredSymbol(declaration.Parent, cancellationToken);
62+
if (symbol is null)
63+
{
64+
return document.Project.Solution;
65+
}
5466
Solution solution = document.Project.Solution;
5567
SymbolRenameOptions options = new()
5668
{

IntelliTect.Analyzer/IntelliTect.Analyzer.Integration.Tests/AnalyzerTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ public static async Task ProcessProject(FileInfo projectFile)
3434
using var workspace = MSBuildWorkspace.Create();
3535
Project project = await workspace.OpenProjectAsync(projectFile.FullName).ConfigureAwait(false);
3636

37-
CompilationWithAnalyzers compilationWithAnalyzers = (await project.GetCompilationAsync().ConfigureAwait(false))
37+
Compilation compilation = await project.GetCompilationAsync().ConfigureAwait(false)
38+
?? throw new InvalidOperationException("Could not get compilation");
39+
CompilationWithAnalyzers compilationWithAnalyzers = compilation
3840
.WithAnalyzers(ImmutableArray.Create(GetAnalyzers().ToArray()));
3941

4042
ImmutableArray<Diagnostic> diags = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().ConfigureAwait(false);

IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Helpers/CodeFixVerifier.Helper.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Linq;
34
using System.Threading;
@@ -26,7 +27,8 @@ private static async Task<Document> ApplyFix(Document document, CodeAction codeA
2627
{
2728
System.Collections.Immutable.ImmutableArray<CodeActionOperation> operations = await codeAction.GetOperationsAsync(CancellationToken.None);
2829
Solution solution = operations.OfType<ApplyChangesOperation>().Single().ChangedSolution;
29-
return solution.GetDocument(document.Id);
30+
return solution.GetDocument(document.Id)
31+
?? throw new InvalidOperationException("Could not get document from solution");
3032
}
3133

3234
/// <summary>
@@ -66,7 +68,8 @@ private static IEnumerable<Diagnostic> GetNewDiagnostics(IEnumerable<Diagnostic>
6668
/// <returns>The compiler diagnostics that were found in the code</returns>
6769
private static IEnumerable<Diagnostic> GetCompilerDiagnostics(Document document)
6870
{
69-
return document.GetSemanticModelAsync().Result.GetDiagnostics();
71+
return (document.GetSemanticModelAsync().Result
72+
?? throw new InvalidOperationException("Could not get semantic model")).GetDiagnostics();
7073
}
7174

7275
/// <summary>
@@ -77,7 +80,8 @@ private static IEnumerable<Diagnostic> GetCompilerDiagnostics(Document document)
7780
private static string GetStringFromDocument(Document document)
7881
{
7982
Document simplifiedDoc = Simplifier.ReduceAsync(document, Simplifier.Annotation).Result;
80-
SyntaxNode root = simplifiedDoc.GetSyntaxRootAsync().Result;
83+
SyntaxNode root = simplifiedDoc.GetSyntaxRootAsync().Result
84+
?? throw new InvalidOperationException("Could not get syntax root");
8185
root = Formatter.Format(root, Formatter.Annotation, simplifiedDoc.Project.Solution.Workspace);
8286
return root.GetText().ToString();
8387
}

IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Helpers/DiagnosticVerifier.Helper.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyz
6161
var diagnostics = new List<Diagnostic>();
6262
foreach (Project project in projects)
6363
{
64-
CompilationWithAnalyzers compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer));
64+
CompilationWithAnalyzers compilationWithAnalyzers = (project.GetCompilationAsync().Result
65+
?? throw new InvalidOperationException("Could not get compilation"))
66+
.WithAnalyzers(ImmutableArray.Create(analyzer));
6567
ImmutableArray<Diagnostic> diags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result;
6668
foreach (Diagnostic diag in diags)
6769
{
@@ -74,7 +76,7 @@ protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyz
7476
for (int i = 0; i < documents.Length; i++)
7577
{
7678
Document document = documents[i];
77-
SyntaxTree tree = document.GetSyntaxTreeAsync().Result;
79+
SyntaxTree? tree = document.GetSyntaxTreeAsync().Result;
7880
if (tree == diag.Location.SourceTree)
7981
{
8082
diagnostics.Add(diag);
@@ -173,7 +175,8 @@ private static Project CreateProject(string[] sources, string language = Languag
173175
count++;
174176
}
175177

176-
return solution.GetProject(projectId);
178+
return solution.GetProject(projectId)
179+
?? throw new InvalidOperationException("Could not get project from solution");
177180
}
178181
#endregion
179182
}

IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Verifiers/CodeFixVerifier.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public abstract partial class CodeFixVerifier : DiagnosticVerifier
2222
/// Returns the codefix being tested (C#) - to be implemented in non-abstract class
2323
/// </summary>
2424
/// <returns>The CodeFixProvider to be used for CSharp code</returns>
25-
protected virtual CodeFixProvider GetCSharpCodeFixProvider()
25+
protected virtual CodeFixProvider? GetCSharpCodeFixProvider()
2626
{
2727
return null;
2828
}
@@ -31,7 +31,7 @@ protected virtual CodeFixProvider GetCSharpCodeFixProvider()
3131
/// Returns the codefix being tested (VB) - to be implemented in non-abstract class
3232
/// </summary>
3333
/// <returns>The CodeFixProvider to be used for VisualBasic code</returns>
34-
protected virtual CodeFixProvider GetBasicCodeFixProvider()
34+
protected virtual CodeFixProvider? GetBasicCodeFixProvider()
3535
{
3636
return null;
3737
}
@@ -73,8 +73,10 @@ protected async Task VerifyBasicFix(string oldSource, string newSource, int? cod
7373
/// <param name="newSource">A class in the form of a string after the CodeFix was applied to it</param>
7474
/// <param name="codeFixIndex">Index determining which codefix to apply if there are multiple</param>
7575
/// <param name="allowNewCompilerDiagnostics">A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied</param>
76-
private static async Task VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int? codeFixIndex, bool allowNewCompilerDiagnostics)
76+
private static async Task VerifyFix(string language, DiagnosticAnalyzer? analyzer, CodeFixProvider? codeFixProvider, string oldSource, string newSource, int? codeFixIndex, bool allowNewCompilerDiagnostics)
7777
{
78+
ArgumentNullException.ThrowIfNull(analyzer);
79+
ArgumentNullException.ThrowIfNull(codeFixProvider);
7880
Document document = CreateDocument(oldSource, language);
7981
Diagnostic[] analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, [document]);
8082
IEnumerable<Diagnostic> compilerDiagnostics = GetCompilerDiagnostics(document);
@@ -106,11 +108,11 @@ private static async Task VerifyFix(string language, DiagnosticAnalyzer analyzer
106108
if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any())
107109
{
108110
// Format and get the compiler diagnostics again so that the locations make sense in the output
109-
document = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result, Formatter.Annotation, document.Project.Solution.Workspace));
111+
document = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result!, Formatter.Annotation, document.Project.Solution.Workspace));
110112
newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));
111113

112114
Assert.Fail($"Fix introduced new compiler diagnostics:{string.Join(Environment.NewLine, newCompilerDiagnostics.Select(d => d.ToString()))}" +
113-
$"{Environment.NewLine}{Environment.NewLine}New document:{Environment.NewLine}{document.GetSyntaxRootAsync().Result.ToFullString()}{Environment.NewLine}");
115+
$"{Environment.NewLine}{Environment.NewLine}New document:{Environment.NewLine}{document.GetSyntaxRootAsync().Result!.ToFullString()}{Environment.NewLine}");
114116
}
115117

116118
//check if there are analyzer diagnostics left after the code fix

IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Verifiers/DiagnosticVerifier.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ public abstract partial class DiagnosticVerifier
1717
/// <summary>
1818
/// Get the CSharp analyzer being tested - to be implemented in non-abstract class
1919
/// </summary>
20-
protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
20+
protected virtual DiagnosticAnalyzer? GetCSharpDiagnosticAnalyzer()
2121
{
2222
return null;
2323
}
2424

2525
/// <summary>
2626
/// Get the Visual Basic analyzer being tested (C#) - to be implemented in non-abstract class
2727
/// </summary>
28-
protected virtual DiagnosticAnalyzer GetBasicDiagnosticAnalyzer()
28+
protected virtual DiagnosticAnalyzer? GetBasicDiagnosticAnalyzer()
2929
{
3030
return null;
3131
}
@@ -85,8 +85,9 @@ protected void VerifyBasicDiagnostic(string[] sources, params DiagnosticResult[]
8585
/// <param name="language">The language of the classes represented by the source strings</param>
8686
/// <param name="analyzer">The analyzer to be run on the source code</param>
8787
/// <param name="expected">DiagnosticResults that should appear after the analyzer is run on the sources</param>
88-
private static void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expected)
88+
private static void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer? analyzer, params DiagnosticResult[] expected)
8989
{
90+
ArgumentNullException.ThrowIfNull(analyzer);
9091
Diagnostic[] diagnostics = GetSortedDiagnostics(sources, language, analyzer);
9192
VerifyDiagnosticResults(diagnostics, analyzer, expected);
9293
}
@@ -213,7 +214,7 @@ private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diag
213214
Assert.IsTrue(location.IsInSource,
214215
$"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostics[i]}{Environment.NewLine}");
215216

216-
string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs", StringComparison.Ordinal) ? "GetCSharpResultAt" : "GetBasicResultAt";
217+
string resultMethodName = diagnostics[i].Location.SourceTree!.FilePath.EndsWith(".cs", StringComparison.Ordinal) ? "GetCSharpResultAt" : "GetBasicResultAt";
217218
Microsoft.CodeAnalysis.Text.LinePosition linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition;
218219

219220
builder.AppendFormat("{0}({1}, {2}, {3}.{4})",

0 commit comments

Comments
 (0)