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

Platform compatibility analyzer #3917

Merged
merged 56 commits into from
Aug 26, 2020
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
9e5e8f1
Platform compatability analyzer draft
buyaa-n Jul 7, 2020
176adea
Support more flow scenarios and add more tests
buyaa-n Jul 9, 2020
9859abe
Enable value content analysis, add few more tests
buyaa-n Jul 9, 2020
0c84c22
Merge conflict
buyaa-n Jul 9, 2020
c7a4bc1
Fix bug caused from merge
buyaa-n Jul 10, 2020
8e84e4e
Merge branch 'master' of https://github.com/dotnet/roslyn-analyzers i…
buyaa-n Jul 10, 2020
42245ba
Enum, property support. Use string comparison ever type, support string
buyaa-n Jul 13, 2020
00ffd24
Excclude conditional check, more refactoring
buyaa-n Jul 17, 2020
5a21e8e
Add/fix tests
buyaa-n Jul 18, 2020
7e7e734
Add more tests
buyaa-n Jul 20, 2020
e80c51a
Osx equals mac and vise versa, add more tests and refactoring
buyaa-n Jul 21, 2020
8c2f910
Apply feedback, more refactoring
buyaa-n Jul 21, 2020
00d17a2
Merge conflict
buyaa-n Jul 21, 2020
84a2fd6
Local function, lambda, even, delegate support and tests
buyaa-n Jul 21, 2020
a375098
Merge conflict
buyaa-n Jul 28, 2020
473ce5c
Support platform name without version
buyaa-n Jul 29, 2020
6ae011e
Apply some review comments
buyaa-n Jul 30, 2020
9a31b5e
Address more feedback
buyaa-n Jul 30, 2020
fb70d09
Add caching, attributes dependency logic
buyaa-n Aug 7, 2020
f210ff4
Rename OS attributes and all references
buyaa-n Aug 7, 2020
3103ec7
Update attributes handling, add/remove new/old guard methods support …
buyaa-n Aug 10, 2020
822ff2c
Fix guard logic with && operator, more refacoring, add more tests
buyaa-n Aug 11, 2020
07a2108
Merge conflict
buyaa-n Aug 11, 2020
6cd038a
Suppress build warning, apply some review comments, add doc/comment
buyaa-n Aug 11, 2020
3916447
Add un versioned diagnostic, add more tests for attributes combinatio…
buyaa-n Aug 12, 2020
21263f2
More tests, review updates
buyaa-n Aug 13, 2020
be8c029
Merging conflict
buyaa-n Aug 13, 2020
28a7cba
Fix bugs 4008, 4009, 4011, apply some feedback, fix failing test
buyaa-n Aug 14, 2020
1eba4b8
Fixing 4012, 4010 and applying more feedback
buyaa-n Aug 14, 2020
63b9156
Perf suggestions
mavasani Aug 15, 2020
7d25d86
Use cache for entire compilation
mavasani Aug 15, 2020
5c9a860
Handle || guards
mavasani Aug 15, 2020
70badce
Fix interprocedural analysis and also restrict when ValueContentAnaly…
mavasani Aug 15, 2020
8d68a29
Lazilly compute needs value content analysis for improved perf
mavasani Aug 15, 2020
c847d9d
Free dictionary after usage remove some unneeded anymore code sections
buyaa-n Aug 15, 2020
c9d1289
Merge branch 'platform_spec_analyzer' of https://github.com/buyaa-n/r…
mavasani Aug 15, 2020
12532fe
Merge conflict
mavasani Aug 15, 2020
7b9276d
Fix bugs in group #2
buyaa-n Aug 16, 2020
60a4dff
Fixed bug #1
buyaa-n Aug 16, 2020
6bc123f
Apply workaround for https://github.com/dotnet/roslyn/issues/46859
mavasani Aug 16, 2020
9988385
Add support override #4013, #4020
buyaa-n Aug 17, 2020
077321c
Merge branch 'platform_spec_analyzer' of github.com:buyaa-n/roslyn-an…
buyaa-n Aug 17, 2020
1cf9990
Update messages, add named arguments support
buyaa-n Aug 17, 2020
56acd8a
Fix an override scenario, update named argument logic
buyaa-n Aug 17, 2020
7b388a1
Apply feedback
buyaa-n Aug 17, 2020
04b6a95
Remove ObsoletedOsPlatform attribute
buyaa-n Aug 18, 2020
919cf2c
Use DisplayString name
buyaa-n Aug 18, 2020
5355797
Apply guard methods new logic fix bug
buyaa-n Aug 19, 2020
d9aa6a9
Add more guard tests and fix failures
buyaa-n Aug 20, 2020
d5e1e74
Add Msbuild configuration support
buyaa-n Aug 21, 2020
865071d
Use local instead of static field
buyaa-n Aug 23, 2020
2dd6240
Fix and enable some failing tests
buyaa-n Aug 24, 2020
5335d58
Match string pattern instead of constants for guard method names
buyaa-n Aug 24, 2020
4aed281
Small refactoring updates
buyaa-n Aug 24, 2020
b749dfb
Apply feedbacks
buyaa-n Aug 24, 2020
c252e9b
Lower case 'platform compatibility'
buyaa-n Aug 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ CA1046 | Design | Disabled | DoNotOverloadOperatorEqualsOnReferenceTypes, [Docum
CA1047 | Design | Info | DoNotDeclareProtectedMembersInSealedTypes, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1047)
CA1069 | Design | Info | EnumShouldNotHaveDuplicatedValues, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1069)
CA1070 | Design | Info | DoNotDeclareEventFieldsAsVirtual, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1070)
CA1416 | Interoperability | Info | RuntimePlatformCheckAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1416)
CA1416 | Interoperability | Warning | PlatformCompatabilityAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1416)
CA1417 | Interoperability | Warning | DoNotUseOutAttributeStringPInvokeParametersAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1417)
CA1700 | Naming | Disabled | DoNotNameEnumValuesReserved, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1700)
CA1713 | Naming | Disabled | EventsShouldNotHaveBeforeOrAfterPrefix, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1713)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Diagnostics.CodeAnalysis;
using Analyzer.Utilities;
using Microsoft.CodeAnalysis;

namespace Microsoft.NetCore.Analyzers.InteropServices
{
public sealed partial class PlatformCompatabilityAnalyzer
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
{
private enum PlatformAttributeType
{
None,
MinimumOSPlatformAttribute,
ObsoletedInOSPlatformAttribute,
RemovedInOSPlatformAttribute,
TargetPlatformAttribute
}

private readonly struct PlatformAttributeInfo : IEquatable<PlatformAttributeInfo>
{
public PlatformAttributeType AttributeType { get; }
public string PlatformName { get; }
public Version Version { get; }

private PlatformAttributeInfo(PlatformAttributeType attributeType, string platformName, Version version)
{
AttributeType = attributeType;
PlatformName = platformName;
Version = version;
}

public static bool TryParsePlatformAttributeInfo(AttributeData osAttribute, out PlatformAttributeInfo parsedAttribute)
{
if (!osAttribute.ConstructorArguments.IsEmpty &&
osAttribute.ConstructorArguments[0].Type.SpecialType == SpecialType.System_String &&
!osAttribute.ConstructorArguments[0].IsNull &&
!osAttribute.ConstructorArguments[0].Value.Equals(string.Empty) &&
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
TryParsePlatformNameAndVersion(osAttribute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version))
{
parsedAttribute = new PlatformAttributeInfo(SwitchAttrributeType(osAttribute.AttributeClass.Name), platformName, version);
return true;
}
parsedAttribute = default;
return false;
}

private static PlatformAttributeType SwitchAttrributeType(string osAttributeName)
=> osAttributeName switch
{
MinimumOSPlatformAttribute => PlatformAttributeType.MinimumOSPlatformAttribute,
ObsoletedInOSPlatformAttribute => PlatformAttributeType.ObsoletedInOSPlatformAttribute,
RemovedInOSPlatformAttribute => PlatformAttributeType.RemovedInOSPlatformAttribute,
TargetPlatformAttribute => PlatformAttributeType.TargetPlatformAttribute,
_ => PlatformAttributeType.None,
};

public override bool Equals(object obj)
{
if (obj is PlatformAttributeInfo info)
{
return Equals(info);
}
return false;
}

public override int GetHashCode() => HashUtilities.Combine(AttributeType.GetHashCode(), PlatformName.GetHashCode(), Version.GetHashCode());

public static bool operator ==(PlatformAttributeInfo left, PlatformAttributeInfo right) => left.Equals(right);

public static bool operator !=(PlatformAttributeInfo left, PlatformAttributeInfo right) => !(left == right);
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved

public bool Equals(PlatformAttributeInfo other) =>
AttributeType == other.AttributeType && IsOSPlatformsEqual(PlatformName, other.PlatformName) && Version.Equals(other.Version);
}

private static bool TryParsePlatformNameAndVersion(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version)
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
{
version = null;
osPlatformName = string.Empty;
for (int i = 0; i < osString.Length; i++)
{
if (char.IsDigit(osString[i]))
{
try
{
if (i > 0 && Version.TryParse(osString.Substring(i), out Version? parsedVersion))
{
osPlatformName = osString.Substring(0, i);
version = parsedVersion;
return true;
}
else
{
return false;
}
}
catch (ArgumentException)
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
{
// When version string was not valid we don't want to do furter diagnostics
return false;
}
}
}
osPlatformName = osString;
version = new Version(0, 0);
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Microsoft.NetCore.Analyzers.InteropServices
{
public sealed partial class RuntimePlatformCheckAnalyzer
public sealed partial class PlatformCompatabilityAnalyzer
{
private sealed class OperationVisitor : GlobalFlowStateDataFlowOperationVisitor
{
Expand All @@ -34,16 +34,39 @@ public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDeleg
{
var value = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, visitedArguments, invokedAsDelegate, originalOperation, defaultValue);

if (_platformCheckMethods.Contains(method.OriginalDefinition) &&
!visitedArguments.IsEmpty)
if (_platformCheckMethods.Contains(method.OriginalDefinition) && !visitedArguments.IsEmpty)
{
return RuntimeOSPlatformInfo.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResultOpt, _osPlatformType, out var platformInfo) ?
return RuntimeMethodValue.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResultOpt, _osPlatformType, out var platformInfo) ?
new GlobalFlowStateAnalysisValueSet(platformInfo) :
GlobalFlowStateAnalysisValueSet.Unknown;
}

return GetValueOrDefault(value);
}

public override GlobalFlowStateAnalysisValueSet VisitPropertyReference(IPropertyReferenceOperation operation, object? argument)
{
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
var value = base.VisitPropertyReference(operation, argument);
return GetValueOrDefault(value);
}

public override GlobalFlowStateAnalysisValueSet VisitFieldReference(IFieldReferenceOperation operation, object? argument)
{
var value = base.VisitFieldReference(operation, argument);
return GetValueOrDefault(value);
}

public override GlobalFlowStateAnalysisValueSet VisitObjectCreation(IObjectCreationOperation operation, object? argument)
{
var value = base.VisitObjectCreation(operation, argument);
return GetValueOrDefault(value);
}

public override GlobalFlowStateAnalysisValueSet VisitEventReference(IEventReferenceOperation operation, object? argument)
{
var value = base.VisitEventReference(operation, argument);
return GetValueOrDefault(value);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.PooledObjects;
using Microsoft.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using Analyzer.Utilities;
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved

namespace Microsoft.NetCore.Analyzers.InteropServices
{
using ValueContentAnalysisResult = DataFlowAnalysisResult<ValueContentBlockAnalysisResult, ValueContentAbstractValue>;

public sealed partial class RuntimePlatformCheckAnalyzer
public sealed partial class PlatformCompatabilityAnalyzer
{
private struct RuntimeOSPlatformInfo : IAbstractAnalysisValue, IEquatable<RuntimeOSPlatformInfo>
private readonly struct RuntimeMethodValue : IAbstractAnalysisValue, IEquatable<RuntimeMethodValue>
{
private RuntimeOSPlatformInfo(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated)
private RuntimeMethodValue(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated)
{
InvokedPlatformCheckMethodName = invokedPlatformCheckMethodName ?? throw new ArgumentNullException(nameof(invokedPlatformCheckMethodName));
PlatformPropertyName = platformPropertyName ?? throw new ArgumentNullException(nameof(platformPropertyName));
Expand All @@ -35,49 +35,60 @@ private RuntimeOSPlatformInfo(string invokedPlatformCheckMethodName, string plat
public bool Negated { get; }

public IAbstractAnalysisValue GetNegatedValue()
=> new RuntimeOSPlatformInfo(InvokedPlatformCheckMethodName, PlatformPropertyName, Version, !Negated);
=> new RuntimeMethodValue(InvokedPlatformCheckMethodName, PlatformPropertyName, Version, !Negated);

public static bool TryDecode(
IMethodSymbol invokedPlatformCheckMethod,
ImmutableArray<IArgumentOperation> arguments,
ValueContentAnalysisResult? valueContentAnalysisResult,
INamedTypeSymbol osPlatformType,
[NotNullWhen(returnValue: true)] out RuntimeOSPlatformInfo? info)
[NotNullWhen(returnValue: true)] out RuntimeMethodValue? info)
{
if (!TryDecodeOSPlatform(arguments, osPlatformType, out var osPlatformProperty) ||
Debug.Assert(!arguments.IsEmpty);

if (arguments[0].Value is ILiteralOperation literal && literal.Type?.SpecialType == SpecialType.System_String)
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
{
if (literal.ConstantValue.HasValue &&
TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version))
{
info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false);
return true;
}
}

if (!TryDecodeOSPlatform(arguments, osPlatformType, out var osPlatformName) ||
!TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var osVersion))
{
// Bail out
info = default;
return false;
}

info = new RuntimeOSPlatformInfo(invokedPlatformCheckMethod.Name, osPlatformProperty.Name, osVersion, negated: false);
info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, osPlatformName, osVersion, negated: false);
return true;
}

private static bool TryDecodeOSPlatform(
ImmutableArray<IArgumentOperation> arguments,
INamedTypeSymbol osPlatformType,
[NotNullWhen(returnValue: true)] out IPropertySymbol? osPlatformProperty)
[NotNullWhen(returnValue: true)] out string? osPlatformName)
{
Debug.Assert(!arguments.IsEmpty);
return TryDecodeOSPlatform(arguments[0].Value, osPlatformType, out osPlatformProperty);
return TryDecodeOSPlatform(arguments[0].Value, osPlatformType, out osPlatformName);
}

private static bool TryDecodeOSPlatform(
IOperation argumentValue,
INamedTypeSymbol osPlatformType,
[NotNullWhen(returnValue: true)] out IPropertySymbol? osPlatformProperty)
[NotNullWhen(returnValue: true)] out string? osPlatformName)
{
if ((argumentValue is IPropertyReferenceOperation propertyReference) &&
propertyReference.Property.ContainingType.Equals(osPlatformType))
{
osPlatformProperty = propertyReference.Property;
osPlatformName = propertyReference.Property.Name;
return true;
}

osPlatformProperty = null;
osPlatformName = null;
return false;
}

Expand Down Expand Up @@ -130,57 +141,36 @@ static bool TryDecodeOSVersionPart(IArgumentOperation argument, ValueContentAnal

public override string ToString()
{
var versionStr = Version.ToString(fieldCount: GetVersionFieldCount(Version));
var result = $"{InvokedPlatformCheckMethodName};{PlatformPropertyName};{versionStr}";
var result = $"{InvokedPlatformCheckMethodName};{PlatformPropertyName};{Version}";
if (Negated)
{
result = $"!{result}";
}

return result;

static int GetVersionFieldCount(Version version)
{
if (version.Revision != 0)
{
return 4;
}

if (version.Build != 0)
{
return 3;
}

if (version.Minor != 0)
{
return 2;
}

return 1;
}
}

public bool Equals(RuntimeOSPlatformInfo other)
public bool Equals(RuntimeMethodValue other)
=> InvokedPlatformCheckMethodName.Equals(other.InvokedPlatformCheckMethodName, StringComparison.OrdinalIgnoreCase) &&
PlatformPropertyName.Equals(other.PlatformPropertyName, StringComparison.OrdinalIgnoreCase) &&
Version.Equals(other.Version) &&
Negated == other.Negated;

public override bool Equals(object obj)
=> obj is RuntimeOSPlatformInfo otherInfo && Equals(otherInfo);
=> obj is RuntimeMethodValue otherInfo && Equals(otherInfo);

public override int GetHashCode()
=> HashUtilities.Combine(InvokedPlatformCheckMethodName.GetHashCode(), PlatformPropertyName.GetHashCode(), Version.GetHashCode(), Negated.GetHashCode());

bool IEquatable<IAbstractAnalysisValue>.Equals(IAbstractAnalysisValue other)
=> other is RuntimeOSPlatformInfo otherInfo && Equals(otherInfo);
=> other is RuntimeMethodValue otherInfo && Equals(otherInfo);

public static bool operator ==(RuntimeOSPlatformInfo left, RuntimeOSPlatformInfo right)
public static bool operator ==(RuntimeMethodValue left, RuntimeMethodValue right)
{
return left.Equals(right);
}

public static bool operator !=(RuntimeOSPlatformInfo left, RuntimeOSPlatformInfo right)
public static bool operator !=(RuntimeMethodValue left, RuntimeMethodValue right)
{
return !(left == right);
}
Expand Down
Loading