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

Adding support for conversion operators to the ConstantAttributeAnalyzer #915

Merged
merged 5 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ CompilationStartAnalysisContext context
),
OperationKind.Argument
);

context.RegisterOperationAction(
ctx => AnalyzeConversion(
ctx,
(IConversionOperation)ctx.Operation,
constantAttribute
),
OperationKind.Conversion
);
}

private static void AnalyzeParameter(
Expand Down Expand Up @@ -119,6 +128,45 @@ ISymbol constantAttribute
);
}

private static void AnalyzeConversion(
OperationAnalysisContext context,
IConversionOperation conversion,
INamedTypeSymbol constantAttribute
) {

IMethodSymbol @operator = conversion.OperatorMethod;
if( @operator is null ) {
return;
}
if( @operator.Parameters.Length != 1 ) {
return;
}

// Operator parameter is not [Constant], so do nothing
IParameterSymbol parameter = @operator.Parameters[ 0 ];
if( !HasAttribute( parameter, constantAttribute ) ) {
return;
}

// Operand is a constant value, so trust it
IOperation operand = conversion.Operand;
if( operand.ConstantValue.HasValue ) {
return;
}

// Operand was defined as [Constant] already, so trust it
ISymbol operandSymbol = conversion.SemanticModel.GetSymbolInfo( operand.Syntax ).Symbol;
if( operandSymbol is not null && HasAttribute( operandSymbol, constantAttribute ) ) {
return;
}

// Operand is not constant, so report it
context.ReportDiagnostic(
descriptor: Diagnostics.NonConstantPassedToConstantParameter,
location: operand.Syntax.GetLocation(),
messageArgs: new[] { parameter.Name }
);
}

/// <summary>
/// Check if the symbol has a specific attribute attached to it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

using System;

namespace SpecTests
{
namespace SpecTests {

using D2L.CodeStyle.Annotations.Contract;

public sealed class Logger {
Expand All @@ -19,8 +19,8 @@ public static void OtherError( string message ) {
}
}

public sealed class Types
{
public sealed class Types {

public static void SomeMethodWithConstantParameter<T>( [Constant] T param1 ) { }
public static void SomeMethodWithParameter<T>( T param1 ) { }
public static void SomeMethodWithOneConstantParameter<T>( [Constant] T param1, T param2 ) { }
Expand All @@ -30,13 +30,44 @@ public static void SomeMethodWithTwoConstantParameters<T>( [Constant] T param1,
public interface IInterface { }
public class SomeClassImplementingInterface : IInterface { }
public static void SomeMethodWithInterfaceParameter( [Constant] IInterface /* InvalidConstantType(Interface) */ @interface /**/ ) { }
public static void SomeMethodWithOneInterfaceParameter<T>(IInterface param1, [Constant] T param2) { }
public static void SomeMethodWithOneInterfaceParameter<T>( IInterface param1, [Constant] T param2 ) { }

public readonly struct ConstantStruct {
public ConstantStruct( [Constant] string value ) {
Value = value;
}
public string Value { get; }
public static implicit operator ConstantStruct( [Constant] string value ) {
return new ConstantStruct( value );
}
public static explicit operator ConstantStruct( [Constant] bool value ) {
return new ConstantStruct( "true" );
}
}

public readonly struct NonConstantStruct {
public NonConstantStruct( string value ) {
Value = value;
}
public string Value { get; }
public static implicit operator NonConstantStruct( string value ) {
return new NonConstantStruct( value );
}
public static explicit operator NonConstantStruct( bool value ) {
return new NonConstantStruct( "true" );
}
}
}

public sealed class Tests
{
void Method()
{
public sealed class Tests {

private static class Constants {
public const bool Bool = true;
public const string String = "foo";
}

void Method() {

#region Invalid type tests
const Types.SomeClassImplementingInterface interfaceClass = new Types.SomeClassImplementingInterface { };
Types.SomeMethodWithConstantParameter<Types.IInterface>( /* NonConstantPassedToConstantParameter(param1) */ interfaceClass /**/ );
Expand Down Expand Up @@ -170,5 +201,84 @@ void Method()
Types.SomeMethodWithTwoConstantParameters<bool>( /* NonConstantPassedToConstantParameter(param1) */ variableBool /**/, /* NonConstantPassedToConstantParameter(param2) */ variableBool /**/ );
#endregion
}

#region Constructor Tests

void ConstructorTests(
[D2L.CodeStyle.Annotations.Contract.Constant] string trusted,
string untrusted
) {
const string constant = "foo";
string variable = "bar";

new Types.ConstantStruct( "abc" );
new Types.ConstantStruct( Constants.String );
new Types.ConstantStruct( constant );
new Types.ConstantStruct( trusted );
new Types.ConstantStruct( /* NonConstantPassedToConstantParameter(value) */ variable /**/ );
new Types.ConstantStruct( /* NonConstantPassedToConstantParameter(value) */ untrusted /**/ );

new Types.NonConstantStruct( "abc" );
new Types.NonConstantStruct( Constants.String );
new Types.NonConstantStruct( constant );
new Types.NonConstantStruct( trusted );
new Types.NonConstantStruct( variable );
new Types.NonConstantStruct( untrusted );
}

#endregion

#region Explicit Operator Tests

void ExplicitOperatorTests(
[D2L.CodeStyle.Annotations.Contract.Constant] bool trusted,
bool untrusted
) {
const bool constant = true;
bool variable = true;

{ Types.ConstantStruct v = (Types.ConstantStruct)true; }
{ Types.ConstantStruct v = (Types.ConstantStruct)Constants.Bool; }
{ Types.ConstantStruct v = (Types.ConstantStruct)constant; }
{ Types.ConstantStruct v = (Types.ConstantStruct)trusted; }
{ Types.ConstantStruct v = (Types.ConstantStruct) /* NonConstantPassedToConstantParameter(value) */ variable /**/; }
{ Types.ConstantStruct v = (Types.ConstantStruct) /* NonConstantPassedToConstantParameter(value) */ untrusted /**/; }

{ Types.NonConstantStruct v = (Types.NonConstantStruct)true; }
{ Types.NonConstantStruct v = (Types.NonConstantStruct)Constants.Bool; }
{ Types.NonConstantStruct v = (Types.NonConstantStruct)constant; }
{ Types.NonConstantStruct v = (Types.NonConstantStruct)trusted; }
{ Types.NonConstantStruct v = (Types.NonConstantStruct)variable; }
{ Types.NonConstantStruct v = (Types.NonConstantStruct)untrusted; }
}

#endregion

#region Implicit Operator Tests

void ImplicitOperatorTests(
[D2L.CodeStyle.Annotations.Contract.Constant] string trusted,
string untrusted
) {
const string constant = "foo";
string variable = "bar";

{ Types.ConstantStruct v = "abc"; }
{ Types.ConstantStruct v = Constants.String; }
{ Types.ConstantStruct v = constant; }
{ Types.ConstantStruct v = trusted; }
{ Types.ConstantStruct v = /* NonConstantPassedToConstantParameter(value) */ variable /**/; }
{ Types.ConstantStruct v = /* NonConstantPassedToConstantParameter(value) */ untrusted /**/; }

{ Types.NonConstantStruct v = "abc"; }
{ Types.NonConstantStruct v = Constants.String; }
{ Types.NonConstantStruct v = constant; }
{ Types.NonConstantStruct v = trusted; }
{ Types.NonConstantStruct v = variable; }
{ Types.NonConstantStruct v = untrusted; }
}

#endregion

}
}
Loading