Giter Club home page Giter Club logo

conventional's Introduction

Conventional Build status NuGet

Conventional provides a suite of ready-made tests for enforcing conventions within your types, assemblies, solutions and databases to make sure your duckies are all in a row.

Conventional targets .NET Standard 2.0, and as of Conventional 7.x, Conventional โค's Linux!

To install from NuGet

Install-Package Best.Conventional

Documentation

To get started with Conventional, please check out the wiki for a comprehensive list of included conventions, sample usages, and configuration information.

Examples

Not sure how to get started with Conventional? Check out the sample repository which contains a bunch of real-world usage examples

Roslyn-based conventions

Build status NuGet

Install-Package Best.Conventional.Roslyn

Rolsyn-based conventions target net6.0. Check out the documentation for more information.

Contributing

Conventional's test suite requires a default named .\SQLEXPRESS instance. If you have another instance you would like to use for development, create a copy of development.settings.example in the solution root and rename to development.settings, and supply your own connection string.

License

Licensed under the terms of the MS-PL license

conventional's People

Contributors

akirayamamoto avatar andrewabest avatar brendankowitz avatar clintonturner avatar dependabot[bot] avatar flakey-bit avatar matt-richardson avatar mderriey avatar michael-wolfenden avatar murlakatam avatar paulegradie avatar rophuine avatar xwipeoutx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

conventional's Issues

Expressions of Interest - MustConformToOneOf for conventions

A recent update to Conventional made property-based conventions more explicit - in particular it ensured that PropertiesMustHavePrivateSettersConventionSpecification didn't allow properties with no setters pass.

Whilst I'm all for conventions being more explicit and there for easier to reason about / apply / rely on, the previous behaviour was also useful in some circumstances - for example you could apply it to Entity Framework models and it will allow navigation properties etc implemented as get-only properties to exist alongside other properties with private setters, giving you stronger guarantees on encapsulation across your models.

If you wanted to achieve the same behaviour now there is no out of the box convention to do so - you're left to write your own convention. This is simple enough to do, the framework is built to encourage you rolling your own to suit your specific needs.

However another option would be to implement a MustConformToOneOf extension that allowed you to group sets of conventions, and ensure that a given type's contents conform to at least one of the conventions specified.

To do this we would need to strongly type ConventionResult so that we could reliably inspect and compare the results emitted by various conventions, and then make decisions on whether a given Type indeed passed at least one of the supplied tests.

The implementation would look something like this:

https://gist.github.com/andrewabest/1df91aef9116488480ad8df4d90e8fa0

I'll leave this issue up for a while - if you're interested in Conventional having this type of functionality, sing out!

Running built-in Roslyn analyzer fails build in Visual Studio 2022

image
Got

AD0001 Analyzer 'Conventional.Roslyn.Analyzers.IfAndElseMustHaveBracesAnalyzer' threw an exception of type 'System.IO.FileNotFoundException' with message 'Could not load file or assembly 'netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified.'.

'Conventional.Roslyn.Analyzers.UsingsStatementsMustNotBeNestedAnalyzer' threw an exception of type 'System.IO.FileNotFoundException' with message 'Could not load file or assembly 'netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified.'.

in latest visual studio 2022 Version 17.7.6 (but works fine in Rider)

Workaround:
Configure project to stop using analyzers in live mode or on build
image
image

Question to owner:

Perhaps its time to upgrade from dotnet standard to .net 6 as well @andrewabest, or multi target to all LTS versions atm, including .netStandard2.1?

Reasoning: to be able to run those analysers in life analysis mode or on build those analysers dll must target the same target framework as the project that is pulling the nuget package.

I can make that change if you are OK with this proposed solution...

https://github.com/andrewabest/Conventional/blob/14c7302594621c3b1b628067612a7f4ec16a3857/src/Roslyn/Conventional.Roslyn.Analyzers/Conventional.Roslyn.Analyzers.csproj#L4C6-L4C6

  <PropertyGroup>
        <TargetFrameworks>netstandard2.1;net6.0;net7.0</TargetFrameworks>
        <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
    </PropertyGroup>

for both Roslyn and Analyzers projects.

Reference to System.Data.SqlClient causes breakage on .NET 8

When testing with .NET 8 RC2, I found that some code broke with this exception:

---- System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types.
Could not load type 'SqlGuidCaster' from assembly 'System.Data.SqlClient, Version=4.6.1.5, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.

This is a known issue in SqlClient, raised in Feb 23 at dotnet/SqlClient#1930

SqlClient have fixed the issue in their PR#1934 (this specific commit) and the fix is available in Microsoft.Data.SqlClient v5.2.0-preview3 or greater.

There is no current fix for the older System.Data.SqlClient. I'm guessing this is because it appears to have been superseded by Microsoft.Data.SqlClient

This is relevant for Conventional because conventional pulls in System.Data.SqlClient and can trigger the error in codebases that don't use any of the Sql functionality.

Logically, if codebases aren't using the Sql functionality then they shouldn't cause an assembly load on SqlClient, however this codebase happened to be doing reflection-based assembly scanning which caused the assembly load. Unfortunately we're not able to stop the assembly scanning as it is done by another third party library (in this case, Nuke)


Beware upgrading the SqlClient reference:

Right now, if conventional changes to reference Microsoft.Data.SqlClient , the latest release is 5.1 which is broken on .NET 8. It needs a preview of 5.2 which isn't ready yet.

If conventional references this, it may cause people to unknowingly pull the assembly into their test suites, which may break when they go to .NET 8.

They'll need to edit their csproj's and add an explicit reference to 5.2 to force an upgrade.

If they're using the Database stuff this seems fair and reasonable (They'll already have their own SqlClient references that they can change), but if someone isn't using the Database parts of Conventional, then needing to manually bump an invisible SqlClient version seems like a pretty indirect and non-obvious fix.

It may be a good idea to wait until a non-preview version 5.2 of Microsoft.Data.SqlClient is available before moving forward with this, and consider any other effects that may have (e.g. older .NET version compatibility)

.NET Core / netstandard support

Given the current state of netstandard, with 1.5 and 1.6 being of 'questionable' long term stability, and that the 'stable' netstandard version, 1.4, does not have enough of the System.Reflection API to be useful in Conventional, I'm going to put the brakes on the conversion until netstandard 2.0 is released in Q3 2017.

This will also give Cecil time to come out of beta with its .NET Core support.

Support a 'for all assemblies' syntax

I want to apply the same assembly convention to all projects in my solution.

Currently the TheAssembly static helper method only supports returning a single Assembly, and in fact throws an exception if you put a wildcard it because it recognises multiple matches.

My current solution was to write code like this:

public void ProjectsMustNotReferenceDllsFromBinOrObjDirectories()
{
    var candidates = Directory.GetFiles(KnownPaths.SolutionRoot, "*.csproj", SearchOption.AllDirectories)
                                        .Select(path => new AssemblySpecimen(path));

    if (candidates.Any() == false)
    {
        throw new ConventionException("Could not locate a project file matching the pattern *.csproj");
    }

    foreach (var asm  in candidates)
    {        
        asm.MustConformTo(Convention.MustNotReferenceDllsFromBinOrObjDirectories).WithFailureAssertion(Assert.Fail);
    }
}

In general it would be great to apply conventions to collections of things.

Assembly (project) conventions should support Directory.Build.Props & Directory.Build.Targets

MSBuild allows templating / "meta-programming" of csproj files via Directory.Build.Props & Directory.Build.Targets - see this page for more background but basically

  • Directory.Build.Props allows setting default property values across all projects in the directory (recursively)
  • Directory.Build.Targets allows setting dependant property values (i.e. incorporating customizations from individual projects)

At least that's my understanding - haven't used Directory.Build.Targets myself.

Existing conventions like MustNotReferenceDllsFromTransientOrSdkDirectoriesConventionSpecification that parse the project XML are unaware of this functionality and are arguably broken as a result.

For example, a Directory.Build.Props file could add an (assembly) Reference w/ a HintPath to all projects in the directory, which MustNotReferenceDllsFromTransientOrSdkDirectoriesConventionSpecification would be unaware of when examing the project XML, leading to false-negative.

Github Actions + Containerize build process

At the moment Conventional is built on Appveyor.

We'd like to bring the build across to GHA as it will make it easier for contributors and consumers to work with.

At the same time, it would be good to containerize the build and its dependencies (i.e. SQL server) so that we can run a MacOS build as well, as Conventional has users on MacOS and they occasionally hit issues (like this one) that we are not testing for due to not having an Mac build.

PropertiesMustHavePrivateSetters convention passes for get-only properties

Perhaps a side-effect of the introduction of get-only properties in some version of C# released since this library was a thing. This convention is slightly ambiguous as it asserts that properties must have private setters - implying a setter should exist - but in fact passes when no setter exists.

Unsure of a good way forward:

  1. Expected behaviour?
  2. Change it to fail when no setter exists - likely a big impact
  3. Add a new convention that ensures a private setter exists?

The reason for this is that PropertyInfo.CanWrite is false for get-only properties. I've created my own called PropertiesMustHaveSettersThatArePrivate that omits the CanWrite check :).

PropertiesMustHavePublic(Getters/Setters) only inspects public properties

I have a test class

    public class AddNewWebEnquiryCommand
    {
	int EnquiryDetails { get; set; }
    }

(EnquiryDetails was simplified to an int to eliminate objects from the equation)

and a test case

    [Test]
    public void MustHavePublicGettersAndSettersOnProperties()
    {
         new[] {busCommandType}
	        .MustConformTo(Convention.PropertiesMustHavePublicGetters)
	        .AndMustConformTo(Convention.PropertiesMustHavePublicSetters)
                .WithFailureAssertion(Assert.Fail);
    }

I had expected this unit test to fail because there is no protection specified on the EnquiryDetails, but instead the test passed, digging around the code, it seems that the Specifications are only examining public properties.
https://github.com/andrewabest/Conventional/blob/master/Conventional/Conventions/PropertiesMustHavePublicGettersConventionSpecification.cs#L16

I believe that GetProperties should be specifying some BindingFlags in order to examine all the properties of a class.

KnownPaths solution-root finding is broken on Linux/OSX

Running the tests on a Linux build agent/container fails to find the solution root due to hard-coded use of backslash as the directory separator.

  • For DefaultSolutionRootFinder we could use $"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}".
    • Alternatively, could do a string split on Path.DirectorySeparatorChar then Path.Combine the parts up to but not including the last occurrence of bin
  • For DefaultSolutionRoot we could use Directory.GetParent(KnownPaths.SolutionRootFinder(AppContext.BaseDirectory)).FullName

The KnownPaths.SolutionRoot setter is also problematic because it "helpfully" adds a trailing backslash if there isn't one, meaning I can't even work around this issue by setting KnownPaths.SolutionRoot manually.

KnownPaths only works on Windows

The method Convention.MustHaveFilesBeEmbeddedResources fails on non-Windows machines as the KnownPaths class seem to rely on backslashes (and maybe other Windows-isms).

Full stack trace:

 Test method EntitlementsService.Tests.Conventions.DatabaseTests.MigrationScripts_MustBeEmbeddedResources threw exception:
System.ArgumentOutOfRangeException: Length cannot be less than zero.
Parameter name: length
Stack Trace:
    at System.String.Substring(Int32 startIndex, Int32 length)
   at Conventional.KnownPaths.<>c.<.cctor>b__15_1()
   at Conventional.Extensions.AssemblyExtensions.ResolveProjectFilePath(Assembly assembly)
   at Conventional.Conventions.Assemblies.AssemblyConventionSpecification.IsSatisfiedBy(Assembly assembly)
   at Conventional.Conformist.MustConformTo(Assembly assembly, IAssemblyConventionSpecification assemblyConventionSpecification)
   at EntitlementsService.Tests.Conventions.DatabaseTests.MigrationScripts_MustBeEmbeddedResources() in /Users/somepath/DatabaseTests.cs:line 13

GetTypeDefinitionFor does not play nicely with runtime generated types (e.g. coverlet code coverage)

Apologies for the vague issue - we've been running into problems with conventions that use GetTypeDefinitionFor when types are emitted at runtime.

As an example, MustNotCallMethodConventionSpecification (and anything that dervies from it) is affected.

For us, the emitted types are coming from Coverlet.

Coverlet is a cross platform code coverage framework for .NET, with support for line, branch and method coverage

Basically, you add a package reference to coverlet.collector to your test projects only and then run tests with data collection on and it spits out a code-coverage report.

This code (which adds a module tracker) is responsible for emitting the types into the instrumented assembly.

When we run the tests normally (in the IDE or with dotnet test) everything is fine. It's when we run the tests with (Coverlet) data collection enabled

dotnet test --collect="XPlat Code Coverage"

that things break.

We see a test failure like this:

  Error Message:
   System.InvalidOperationException : Could not find type Coverlet.Core.Instrumentation.Tracker.MyCompany.MyApp.BFF.DTOs_cbb8ca5e-3732-4ccd-97be-dc8fcaf369f0 in C:\dev\MyCompany.MyApp.BFF\project\Tests\MyCompany.MyApp.BFF.ConventionTests\bin\Debug\net6.0\MyCompany.MyApp.BFF.DTOs.dll.
Types we can see:  - <Module>
 - Microsoft.CodeAnalysis.EmbeddedAttribute
 - System.Runtime.CompilerServices.NullableAttribute
 - System.Runtime.CompilerServices.NullableContextAttribute
 - {...various types}
 - Coverlet.Core.Instrumentation.Tracker.MyCompany.MyApp.BFF.DTOs_cbb8ca5e-3732-4ccd-97be-dc8fcaf369f0
  Stack Trace:
     at Conventional.Conventions.Cecil.DecompilationCache.<>c__DisplayClass10_0.<GetTypeDefinitionFor>b__0(Type t) in C:\dev\Conventional\src\Core\Conventional\Conventions\Cecil\DecompilationCache.cs:line 127
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Conventional.Conventions.Cecil.DecompilationCache.GetTypeDefinitionFor(Type type) in C:\dev\Conventional\src\Core\Conventional\Conventions\Cecil\DecompilationCache.cs:line 112
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Conventional.Conventions.Cecil.DecompilationCache.GetInstructionsFor(Type type) in C:\dev\Conventional\src\Core\Conventional\Conventions\Cecil\DecompilationCache.cs:line 48
   at Conventional.Conventions.Cecil.DecompilationCache.InstructionsFor(Type type, Boolean includeStateMachine) in C:\dev\Conventional\src\Core\Conventional\Conventions\Cecil\DecompilationCache.cs:line 31
   at Conventional.Conventions.Cecil.MustNotCallMethodConventionSpecification.IsSatisfiedBy(Type type) in C:\dev\Conventional\src\Core\Conventional\Conventions\Cecil\MustNotUseMethodSpecification.cs:line 33
   at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Conventional.Conformist.WithFailureAssertion(IEnumerable`1 results, Action`1 failureAssertion) in C:\dev\Conventional\src\Core\Conventional\Conformist.cs:line 87
   at MyCompany.MyApp.BFF.ConventionTests.Code.CommonConventionTests.Convention_MustNotAccessNewRelicAgentStatically(Assembly assembly) in C:\dev\MyCompany.MyApp.BFF\project\Tests\MyCompany.MyApp.BFF.ConventionTests\Code\CommonConventionTests.cs:line 64

Note that the type it was looking for (Coverlet.Core.Instrumentation.Tracker.MyCompany.MyApp.BFF.DTOs_cbb8ca5e-3732-4ccd-97be-dc8fcaf369f0 in the example above) was in the list "Types we can see" (and is evidently in the module definition).

For whatever reason, ModuleDefinition::GetType() (Cecil) refuses to return the TypeDefinition.

We could work around it with something like moduleDefinition.Types.Single(mt => mt.FullName == type.FullName);, however that's missing the point - these generated types are an artefact of the tests running with code coverage on. Almost certainly, the fix is to exclude such types from convention checks.

An example of a convention test (theory) we had problems with:

  [Theory]
  [MemberData(nameof(EnumerateNonTestAssemblies))]
  public void Convention_MustNotAccessNewRelicAgentStatically(Assembly assembly)
  {
      // NewRelic agent (IAgent) should be injected rather than accessed statically via NewRelic.GetAgent()
      assembly.GetTypes()
          .Where(t => ((t.DeclaringType ?? t) != typeof(Startup)))
          .MustConformTo(new MustNotAccessNewRelicAgentStatically())
          .WithFailureAssertion(ConventionHelpers.Fail);
  }

(MustNotAccessNewRelicAgentStatically is based on MustNotCallMethodConventionSpecification).

We can work-around the problem by further filtering the types (from the assembly) e.g.

assembly.GetTypes()
    .Where(t => ((t.DeclaringType ?? t) != typeof(Startup)))
    .Where(type => type.FullName != null && !type.FullName.StartsWith("Coverlet"))

Wondering if this is the best approach or whether the Conventional library could help out with this in some way - thoughts?

TryGetSemanticModel doesn't seem to resolve the semantic model correctly

Description

I've found (unfortunately) that the TryGetSemanticModel approach to resolving semantic models from a given document does not actually work ๐Ÿ˜“. I attempted to find workarounds, however nothing appears to solve the problem.

I've implemented a reproduction of the problem for you to review - but the short of it is: The tests that I've written in the repo suggest that we can swap the TryGetSemanticModel out with GetSemanticModelAsync().Result.

Testing:

  • The Conventional.Roslyn solution is a bit funky and refuses to build for me, so I haven't created a nuget package locally and run an end-to-end test on this
  • The repro below demonstrates the efficacy of the new approach - hopefully that satisfies. But - it would be prudent perhaps to do the E2E test on your end.

Either way - this update can't make any thing worse. :D

Reproduction:

https://github.com/paulegradie/Conventional.Repros/blob/main/SemanticModelNotFound/SemanticModelNotFound.Tests/SemanticModelNotFoundTest.cs

Proposed new conventions

Hey ๐Ÿ‘‹

I was thinking about contributing back to the project with some conventions that I've used in various repos at my organisation. But before I open a PR (or PRs), I thought I'd check if these conventions would fit the project goals - happy to cherry-pick ๐Ÿ™‚

Also, are tests for the conventions a must-have or a nice-to-have?

MvcControllersMustNotUseForbid

What does it do: Checks that controllers deriving from (ASP.NET Core ControllerBase) don't call the Forbid virtual method.

Why would you want that: If you're relaying forbidden from an upstream API you might want to avoid interactions with ASP.NET authentication code. You also might want to return a ProblemDetails response

  • Derives from MustNotCallMethodConventionSpecification
  • Would introduce a dependency on Microsoft.AspNetCore.Mvc.Core

Probably not generally applicable to be honest, but I thought I'd check

MustNotUseGuidNewGuid

What does it do: Checks that code doesn't call Guid.NewGuid()

Why would you want that: If you're trying to avoid random data and/or you're trying to write things in a functional way.

  • Derives from MustNotCallMethodConventionSpecification
  • No dependencies

NamespaceMustStartWith

What does it do: Checks that the type is in a namespace beginning with some needle

Why would you want that: In case MustLiveInNamespaceConventionSpecification is not flexible enough for you.

We're using it to partition a namespace e.g.

private const string RequestDtoNamespacePrefix = "Acme.Foobar.BFF.DTOs.Request";
private const string ResponseDtoNamespacePrefix = "Acme.Foobar.BFF.DTOs.Response";

typeof(StatementLine).Assembly.GetExportedTypes()
    .MustConformTo(new NamespaceMustStartWith(RequestDtoNamespacePrefix)
        .Or(new NamespaceMustStartWith(ResponseDtoNamespacePrefix)))
    .WithFailureAssertion(ConventionHelpers.Fail);
  • Derives from ConventionSpecification
  • No dependencies

AssembliesMustBeIncludedInListConventionSpecification

Derives from AssemblyConventionSpecification
What does it do: Checks that an assembly (name) is included in a hard-coded list/set (of assemblies)

Why would you want that: To help synchronise type-level convention checking

For example

// SolutionTests.cs

private AssemblySpecimen[] NonTestApiAssemblies =>
    AllSolutionAssemblies
        .Where(IsNonTestProject)
        .ToArray();

[Theory]
[MemberData(nameof(EnumerateWarningsToBeTreatedAsErrors))]
public void NonTestProjectsMustTreatWarningAsError(string warning)
{
    NonTestApiAssemblies
        .ToArray()
        .MustConformTo(new TreatsWarningAsErrorConventionSpecification(warning))
        .WithFailureAssertion(ConventionHelpers.Fail);
}

[Fact]
public void NonTestProjectsMustBeIncludedInNonTestAssembliesList()
{
    // We have an (admittedly small) set of convention tests we want to run across all non-test assemblies
    // This test enforces that list of assemblies is up-to-date
    NonTestApiAssemblies
        .ToArray()
        .MustConformTo(
            new AssembliesMustBeIncludedInListConventionSpecification(
                CommonConventionTests.NonTestAssemblies,
                $"{nameof(CommonConventionTests)}:{nameof(CommonConventionTests.NonTestAssemblies)}"
            ))
        .WithFailureAssertion(ConventionHelpers.Fail);
}

// CommonConventionTests.cs

[Theory]
[MemberData(nameof(EnumerateNonTestAssemblies))]
public void Convention_MustNotAccessNewRelicAgentStatically(Assembly assembly)
{
    // NewRelic agent (IAgent) should be injected rather than accessed statically via NewRelic.GetAgent()
    assembly.GetTypes()
        .Where(t => ((t.DeclaringType ?? t) != typeof(Startup)))
        .Where(ConventionHelpers.TypeIsNotGeneratedByCoverlet)
        .MustConformTo(new MustNotAccessNewRelicAgentStatically())
        .WithFailureAssertion(ConventionHelpers.Fail);
}

public static IEnumerable<object[]> EnumerateNonTestAssemblies => NonTestAssemblies.Select(a => new[] { a });

AssemblyReferenceConventionSpecification

Abstract type

MustReferenceAssemblyConventionSpecification

What does it do: Checks that an assembly includes a reference to another assembly (either project reference or package reference)

Derives from AssemblyReferenceConventionSpecification

MustNotReferenceAssemblyConventionSpecification

What does it do: Checks that an assembly does not include a reference to another assembly

Derives from AssemblyReferenceConventionSpecification

MustNotIncludeProjectReferencesConventionSpecification

What does it do: Checks that a project doesn't reference any other projects
Why would you want that: Avoiding bringing in types unintentionally

Derives from AssemblyReferenceConventionSpecification

ProjectMustSpecifyPropertyValueConventionSpecification

What does it do: Checks that a project sets a property in the .csproj

TreatsWarningAsErrorConventionSpecification

What does it do: Checks that a project treats a given warning as an error (via WarningsAsErrors)
Why would you want that: Perhaps you want to enforce that non-test projects treat specific warnings (e.g. possible null reference) as errors.

KnownPaths: Azure directory structure breaks DefaultSolutionRootFinder.

DefaultSolutionRootFinder = x => x.Substring(0, x.LastIndexOf("\\bin\\", StringComparison.Ordinal));

VSTS build agents don't seem to have a \\bin\\ directory, causing this to fail on static construction.
The main issue is that it throws before being able to reassign SolutionRootFinder.

Maybe:

DefaultSolutionRootFinder = x => 
{
   var index = x.LastIndexOf("\\bin\\", StringComparison.Ordinal);
   if (index < 0)   
      return String.Empty;
   else   
      return x.Substring(0, index);
}

I can put this in a PR if you'd like, or let me know if I've messed up somewhere.

How to implement an async convention that requires Mono.Cecil

I was thinking of adding a new async convention that for library code checks whether all tasks that are awaited have ConfigureAwait(false) set.

This requires the use of Mono.Cecil to detect, however the conventions are split into seperate 'Async' and 'Cecil' projects.

What do you suggest?

MustOnlyContainInformativeCommentsConventionSpecification incorrectly identifies some Url schemas

The MustOnlyContainInformativeCommentsConventionSpecification is a little bit aggressive, for example

var query = new Query("ftp://");

as well as arbitrary // in strings (that aren't http(s)://) causes the test to fail.

I'm not sure what the best way round this is (maybe something like "if it's within quotes discount it").

I can write up a minimal example of this issue/a test case if that's helpful?

Async breaks MustNotResolveCurrentTimeViaDateTimeConventionSpecification

Hi there!

Given a type:

public class Foo
{
    public void Bar()
    {
        var thing = DateTime.Now;
    }
}

the following IL is generated:

Foo.Bar:
IL_0000:  nop         
IL_0001:  call        System.DateTime.get_Now
IL_0006:  stloc.0     // thing
IL_0007:  ret  

which is easily detected by the MustNotResolveCurrentTimeViaDateTimeConventionSpecification which searches for any get_Now calls on DateTime.

What happens if this method is made async though (ignoring the void return type)?

public class Foo
{
    public async void Bar()
    {
        var thing = DateTime.Now;
    }
}

Foo.Bar now looks like:

Foo.Bar:
IL_0000:  newobj      UserQuery+Foo+<Bar>d__0..ctor
IL_0005:  stloc.0     
IL_0006:  ldloc.0     
IL_0007:  ldarg.0     
IL_0008:  stfld       UserQuery+Foo+<Bar>d__0.<>4__this
IL_000D:  ldloc.0     
IL_000E:  call        System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create
IL_0013:  stfld       UserQuery+Foo+<Bar>d__0.<>t__builder
IL_0018:  ldloc.0     
IL_0019:  ldc.i4.m1   
IL_001A:  stfld       UserQuery+Foo+<Bar>d__0.<>1__state
IL_001F:  ldloc.0     
IL_0020:  ldfld       UserQuery+Foo+<Bar>d__0.<>t__builder
IL_0025:  stloc.1     
IL_0026:  ldloca.s    01 
IL_0028:  ldloca.s    00 
IL_002A:  call        System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Start<<Bar>d__0>
IL_002F:  ret

with the call to DateTime.Now pushed to the state machine, here it's the first step:

d__0.MoveNext:
IL_0000:  ldarg.0     
IL_0001:  ldfld       UserQuery+Foo+d__0.<>1__state
IL_0006:  stloc.0     
IL_0007:  nop         
IL_0008:  ldarg.0     
IL_0009:  call        System.DateTime.get_Now
IL_000E:  stfld       UserQuery+Foo+d__0.5__1
IL_0013:  leave.s     IL_002D
IL_0015:  stloc.1     
IL_0016:  ldarg.0     
IL_0017:  ldc.i4.s    FE 
IL_0019:  stfld       UserQuery+Foo+d__0.<>1__state
IL_001E:  ldarg.0     
IL_001F:  ldflda      UserQuery+Foo+d__0.<>t__builder
IL_0024:  ldloc.1     
IL_0025:  call        System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException
IL_002A:  nop         
IL_002B:  leave.s     IL_0041
IL_002D:  ldarg.0     
IL_002E:  ldc.i4.s    FE 
IL_0030:  stfld       UserQuery+Foo+d__0.<>1__state
IL_0035:  ldarg.0     
IL_0036:  ldflda      UserQuery+Foo+d__0.<>t__builder
IL_003B:  call        System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult
IL_0040:  nop         
IL_0041:  ret

This makes the convention pass when it shouldn't, as the call to the prohibited method is now in a separate type to that that the search is being performed on.

Is there some way to follow the generated state machine's type and search within the generated code? Maybe this is a convention better suited to roslyn, as it has the raw syntax tree?

Removing .NET 4.0 Support

I'm looking to introduce .NET core support.

In doing so, to avoid the big headache of multi-targeted code, I'd like to move away from .NET 4.0, and only support .NET 4.5+.

This will also clean up Conventional's codebase quite a bit, and make contributing to it easier, as I could merge the .Net 4.5 projects back into the core projects, and not need to link files all over the place.

Throwing this out to see if anyone has any objections to this?

This would bump Conventional into 2.x. Anyone still requiring .NET 4.0 support could still use 1.x.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.