Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dotnet] Enable NRT on exceptional types #14672

Open
wants to merge 10 commits into
base: trunk
Choose a base branch
from

Conversation

RenderMichael
Copy link
Contributor

@RenderMichael RenderMichael commented Oct 29, 2024

User description

Description

Adds nullable reference type annotations to exceptional types, such as exception types.

This also includes the polyfill nullability attributes, so this PR may block some other nullability work.

Contributes to #14640

Motivation and Context

Some easy wins on types whose nullability status is fairly unambiguous.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • I have read the contributing document.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

PR Type

enhancement


Description

  • Enabled nullable reference types across multiple files to improve null safety.
  • Updated exception classes to use nullable types for constructor parameters.
  • Added polyfill for nullable attributes to ensure compatibility with older .NET versions.
  • Enhanced FirefoxDriver with nullable reference types and added MemberNotNullWhen attribute.
  • Updated IFileDetector and DefaultFileDetector with NotNullWhen attribute for method parameters.

PRDescriptionHeader.CHANGES_WALKTHROUGH

Relevant files
Enhancement
15 files
DefaultFileDetector.cs
Enable nullable reference types and add NotNullWhen attribute

dotnet/src/webdriver/DefaultFileDetector.cs

  • Enabled nullable reference types.
  • Added NotNullWhen attribute to IsFile method parameter.
  • +5/-1     
    DetachedShadowRootException.cs
    Enable nullable reference types for exception class           

    dotnet/src/webdriver/DetachedShadowRootException.cs

    • Enabled nullable reference types.
    +2/-1     
    CommandResponseException.cs
    Update exception constructors with nullable parameters     

    dotnet/src/webdriver/DevTools/CommandResponseException.cs

  • Enabled nullable reference types.
  • Updated constructor parameters to nullable types.
  • +4/-2     
    DriverServiceNotFoundException.cs
    Update exception constructors with nullable parameters     

    dotnet/src/webdriver/DriverServiceNotFoundException.cs

  • Enabled nullable reference types.
  • Updated constructor parameters to nullable types.
  • +5/-4     
    ElementClickInterceptedException.cs
    Update exception constructors with nullable parameters     

    dotnet/src/webdriver/ElementClickInterceptedException.cs

  • Enabled nullable reference types.
  • Updated constructor parameters to nullable types.
  • +4/-3     
    ElementNotInteractableException.cs
    Update exception constructors with nullable parameters     

    dotnet/src/webdriver/ElementNotInteractableException.cs

  • Enabled nullable reference types.
  • Updated constructor parameters to nullable types.
  • +5/-4     
    ElementNotSelectableException.cs
    Update exception constructors with nullable parameters     

    dotnet/src/webdriver/ElementNotSelectableException.cs

  • Enabled nullable reference types.
  • Updated constructor parameters to nullable types.
  • +5/-4     
    ElementNotVisibleException.cs
    Update exception constructors with nullable parameters     

    dotnet/src/webdriver/ElementNotVisibleException.cs

  • Enabled nullable reference types.
  • Updated constructor parameters to nullable types.
  • +4/-3     
    ErrorResponse.cs
    Update ErrorResponse with nullable fields and parameters 

    dotnet/src/webdriver/ErrorResponse.cs

  • Enabled nullable reference types.
  • Updated fields and constructor parameters to nullable types.
  • +10/-8   
    FirefoxDriver.cs
    Enhance FirefoxDriver with nullable reference types           

    dotnet/src/webdriver/Firefox/FirefoxDriver.cs

  • Enabled nullable reference types.
  • Added MemberNotNullWhen attribute for HasActiveDevToolsSession.
  • Updated method parameters to nullable types.
  • +22/-3   
    IFileDetector.cs
    Enable nullable reference types and add NotNullWhen attribute

    dotnet/src/webdriver/IFileDetector.cs

  • Enabled nullable reference types.
  • Added NotNullWhen attribute to IsFile method parameter.
  • +5/-1     
    InsecureCertificateException.cs
    Update exception constructors with nullable parameters     

    dotnet/src/webdriver/InsecureCertificateException.cs

  • Enabled nullable reference types.
  • Updated constructor parameters to nullable types.
  • +5/-4     
    NullableAttributes.cs
    Add polyfill for nullable attributes                                         

    dotnet/src/webdriver/Internal/NullableAttributes.cs

    • Added polyfill for nullable attributes for compatibility.
    +149/-0 
    InvalidCookieDomainException.cs
    Update exception constructors with nullable parameters     

    dotnet/src/webdriver/InvalidCookieDomainException.cs

  • Enabled nullable reference types.
  • Updated constructor parameters to nullable types.
  • +4/-3     
    InvalidElementStateException.cs
    Update exception constructors with nullable parameters     

    dotnet/src/webdriver/InvalidElementStateException.cs

  • Enabled nullable reference types.
  • Updated constructor parameters to nullable types.
  • +4/-3     

    💡 PR-Agent usage: Comment /help "your question" on any pull request to receive relevant information

    Copy link
    Contributor

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Possible Bug
    The GetContext() method may throw a NullReferenceException if the Execute method returns a null value.

    Code Smell
    The GetFullPageScreenshot() method assumes the Value property of screenshotResponse is not null, which may lead to a NullReferenceException if it is.

    Code Smell
    The constructor assumes that certain dictionary keys exist and their values are not null, which may lead to KeyNotFoundException or NullReferenceException if these assumptions are not met.

    Copy link
    Contributor

    codiumai-pr-agent-pro bot commented Oct 29, 2024

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Score
    Possible issue
    Use null-coalescing operator to handle potential null values when converting to string

    Consider using a null-coalescing operator (??) instead of the null-conditional
    operator (?.) followed by ToString(). This will provide a default empty string if
    the Value is null, avoiding potential null reference exceptions.

    dotnet/src/webdriver/Firefox/FirefoxDriver.cs [266]

    -string? response = this.Execute(GetContextCommand, null).Value.ToString();
    +string response = this.Execute(GetContextCommand, null).Value?.ToString() ?? string.Empty;
    • Apply this suggestion
    Suggestion importance[1-10]: 9

    Why: The suggestion correctly replaces the null-conditional operator followed by ToString() with a null-coalescing operator, which is a safer approach to handle potential null values and avoid null reference exceptions. This change enhances the robustness of the code.

    9
    Enhancement
    ✅ Use null-coalescing operator with null-conditional operator for safer string conversion
    Suggestion Impact:The commit implemented a safer string conversion by using the null-conditional operator with pattern matching, which aligns with the suggestion's intention to handle null values safely.

    code diff:

    -                if (responseValue.ContainsKey("message"))
    +                if (responseValue.TryGetValue("message", out object? messageObj)
    +                    && messageObj?.ToString() is string message)
                     {
    -                    if (responseValue["message"] != null)
    -                    {
    -                        this.message = responseValue["message"].ToString() ?? "";
    -                    }
    -                    else
    -                    {
    -                        this.message = "The error did not contain a message.";
    -                    }
    +                    this.message = message;

    Consider using the null-coalescing operator (??) instead of the null-conditional
    operator (?.) followed by ToString(). This will provide a default empty string if
    the value is null, avoiding potential null reference exceptions.

    dotnet/src/webdriver/ErrorResponse.cs [55]

    -this.message = responseValue["message"].ToString() ?? "";
    +this.message = responseValue["message"]?.ToString() ?? string.Empty;
    • Apply this suggestion
    Suggestion importance[1-10]: 9

    Why: This suggestion enhances the code by using both the null-conditional and null-coalescing operators, ensuring that the conversion to string is safe and defaults to an empty string if null. This improves code safety and prevents potential null reference exceptions.

    9
    Use null-forgiving operator to assert non-null value when converting to string

    Consider using the null-forgiving operator (!) instead of the null-conditional
    operator (?) when accessing the Value property of screenshotResponse. This assumes
    that the Value property will never be null in this context, which seems to be the
    case given the nature of the GetFullPageScreenshot method.

    dotnet/src/webdriver/Firefox/FirefoxDriver.cs [394]

    -string base64 = screenshotResponse.Value.ToString()!;
    +string base64 = screenshotResponse.Value!.ToString();
    • Apply this suggestion
    Suggestion importance[1-10]: 8

    Why: The suggestion to use the null-forgiving operator is appropriate here, as it asserts that the Value property is non-null, aligning with the method's context. This change improves code clarity and maintains functionality.

    8
    Use null-conditional operator with null-coalescing operator for more robust null handling

    Consider using the null-coalescing operator (??) instead of the null-conditional
    operator (?.) followed by the null-coalescing operator (??) for a more concise
    expression.

    dotnet/src/webdriver/StackTraceElement.cs [54-73]

    -this.className = elementAttributes["className"].ToString() ?? "";
    -this.methodName = elementAttributes["methodName"].ToString() ?? "";
    -this.fileName = elementAttributes["fileName"].ToString() ?? "";
    +this.className = elementAttributes["className"]?.ToString() ?? "";
    +this.methodName = elementAttributes["methodName"]?.ToString() ?? "";
    +this.fileName = elementAttributes["fileName"]?.ToString() ?? "";
    • Apply this suggestion
    Suggestion importance[1-10]: 8

    Why: The suggestion improves the robustness of null handling by using the null-conditional operator, which is more concise and prevents potential null reference exceptions. This enhances code readability and maintainability.

    8
    ✅ Use pattern matching for more concise type checking and casting
    Suggestion Impact:The commit implemented pattern matching for type checking and casting for the "lineNumber" attribute, making the code more concise.

    code diff:

    -                if (elementAttributes.ContainsKey("lineNumber"))
    +                if (elementAttributes.TryGetValue("lineNumber", out object? lineNumberObj))
                     {
    -                    int line = 0;
    -                    if (int.TryParse(elementAttributes["lineNumber"].ToString(), out line))
    +                    if (int.TryParse(lineNumberObj?.ToString(), out int line))
                         {
                             this.lineNumber = line;
                         }

    Consider using pattern matching with 'is' keyword for type checking and casting in a
    single step, which can make the code more concise and readable.

    dotnet/src/webdriver/StackTraceElement.cs [62-69]

    -if (elementAttributes.ContainsKey("lineNumber"))
    +if (elementAttributes.TryGetValue("lineNumber", out var lineNumberObj) && lineNumberObj is int lineNumber)
     {
    -    int lineNumber;
    -    if (int.TryParse(elementAttributes["lineNumber"].ToString(), out lineNumber))
    -    {
    -        this.lineNumber = lineNumber;
    -    }
    +    this.lineNumber = lineNumber;
     }
    • Apply this suggestion
    Suggestion importance[1-10]: 7

    Why: The suggestion makes the code more concise and readable by using pattern matching for type checking and casting, which simplifies the logic and reduces the number of lines.

    7
    Best practice
    Use a more general method for checking file existence

    Consider using Path.Exists instead of File.Exists to check for file existence, as
    it's more general and can handle both files and directories.

    dotnet/src/webdriver/Remote/LocalFileDetector.cs [40]

    -return File.Exists(keySequence);
    +return !string.IsNullOrEmpty(keySequence) && Path.Exists(keySequence);
    • Apply this suggestion
    Suggestion importance[1-10]: 6

    Why: The suggestion to use Path.Exists instead of File.Exists is more general and can handle both files and directories, enhancing the flexibility of the code.

    6

    💡 Need additional feedback ? start a PR chat

    @@ -372,7 +391,7 @@ public void UninstallAddOn(string addOnId)
    public Screenshot GetFullPageScreenshot()
    {
    Response screenshotResponse = this.Execute(GetFullPageScreenshotCommand, null);
    string base64 = screenshotResponse.Value.ToString();
    string base64 = screenshotResponse.Value.ToString()!;
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    We should double-check whether .Value might be null. I mean not here, in general. Probably Value always not null.

    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    More validation is music to my ears :)

    There are two real boundaries in Selenium: user input and the server. In the latter case, we basically always have an understanding of the expected response. Maybe we can have a generic Response.GetValue<T>() method that automatically de-serializes? Here it would look like screenshotResponse.GetValue<string>().

    Or perhaps the Execute method can be generic, like so?

    Response<string> screenshotResponse = this.Execute<string>(GetFullPageScreenshotCommand, null);

    In this particular case, we are not actually doing null checking, since it .Value were null, we would get a null reference on ToString(). The reason we suppress the null warning is because an untyped object.ToString() is unfortunately annotated as nullable (almost every type I have seen replaces this with a non-nullable string).

    So a "better" approach might be to cast like string base64 = (string)screenshotResponse.Value;, since we know the response to be a string.

    @RenderMichael
    Copy link
    Contributor Author

    @nvborisenko PR feedback addressed, PTAL

    {
    if (responseValue != null)
    {
    if (responseValue.ContainsKey("message"))
    if (responseValue.TryGetValue("message", out object? messageObj)
    && messageObj?.ToString() is string message)
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    && messageObj is not null instead of && messageObj?.ToString() is string message?

    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    The problem is, untyped object.ToString() is annotated to return a string?. we need to handle that potentially null value too.

    We can suppress it like before, with messageObj.ToString()!

    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Ah, I like objects in selenium.. Looked through where and how this class used, and seems it makes sense to make fields nullable.

    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I reworked the class to allow nullable fields. Let me know if it needs any more changes

    @RenderMichael
    Copy link
    Contributor Author

    @nvborisenko I think everything's been addressed in this PR, it's ready for another look

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    3 participants