A Mono.Gendarme fork, built against a recent Mono.Cecil version, one that can load assemblies built with current compilers. Can be used with the Fake.build plugin.
See the head of the release branch for the features in the latest actual release.
In this branch
- Can load .net core assemblies
- Will search the nuget cache for dependencies, though this can take some time as an alternative to using
dotnet publishto get all the code you want to analyse in one place.
- Will search the nuget cache for dependencies, though this can take some time as an alternative to using
- Will load debug information from embedded symbols or actual
.pdbfiles if available even on non-Windows platforms.- The main impact is that the
AvoidLongMethodsRuleworks by LoC and not IL against .net core code on all platforms.
- The main impact is that the
- Depending whether the Framework or dotnet tool version is used, the results may differ when faced with the same assembly, because of the different runtime being consulted
- e.g. several types marked
[Serializable]in the Framework are not so marked atdotnet, so serialization rules will give different answers
- e.g. several types marked
- Because they use obsolescing functions not present in
netstandard2.0the followingGendarme.Rules.Security.Casrules are only present in the Framework tool build, under theObsolete.Rules.Security.Casname:AddMissingTypeInheritanceDemandRuleDoNotExposeMethodsProtectedByLinkDemandRuleDoNotReduceTypeSecurityOnMethodsRuleSecureGetObjectDataOverridesRule
- The obsolete
Gendarme.Rules.Portability.MonoCompatibilityReviewRuleis not implemented in this fork. DefineAZeroValueRuledoes not trigger for non-int32 enums that have a suitably typed zero value. This rule should not also be doing the job ofEnumsShouldUseInt32Rule- Due to IL changes
UseIsOperatorRulehas been tuned to avoid false positives at the cost of missing some failure cases - Due to IL changes
DoNotAssumeIntPtrSizeRulewill give false negatives for simple (and often implicit) casts ofIntPtrto other integer types. - New rule categories
AltCode.Rules.Generalfor general purpose rulesJustifySuppressionRuleto check theJustificationproperty onSuppressMessageattributePreferStrongNamedAssembliesRuleto replace deprecated/withdrawn FxCop rule Microsoft.Design#CA2210AvoidAssemblySemanticVersionMismatchRuleto insist that the API contract (major, minor, and optionally build if defined for the assembly) match, but the lesser facets, revision and possibly build are free.
AltCode.Rules.PowerShellfor re-implementing the old Microsoft PowerShell FxCop rulesAltCode.Rules.PowerShell.UseOnlyStandardVerbsRuleto replace "Microsoft.PowerShell#PS1001:UseOnlyStandardVerbs"AltCode.Rules.PowerShell.DefineCmdletInTheCorrectNamespaceRuleto replace "Microsoft.PowerShell#PS1011:DefineCmdletInTheCorrectNamespace"
- In the text output, include a specimen global suppression attribute for each issue, for convenience when dealing with remaining intractable issues e.g. arising from code generation
- While
Scopeis not heeded by the Gendarme process, it's there to placate other consumers (which will ignore the foreign rule); the comment indicates the corresponding object type within the Gendarme analysis in case they should ever be out of line. - The syntax and punctuation of the
Targetwith regards to nested types and special names is as Gendarme expects, which differs somewhat from FxCop in annoying details - The emitted section looks like this:
- While
Global Suppression Attribute:
[<assembly: SuppressMessage("Gendarme.Rules.Correctness",
"MethodCanBeMadeStaticRule",
Scope = "member", // MethodDefinition
Target = "ParameterNamesShouldMatch.Handler::ShowMessage(a,System.String)",
Justification = "")>]
After having achieved the first objective, of being able to analyze code from the new .net, the next goal of this fork has been to make the tool more F# aware, because that's where I personally use it the most. There are several places where F# code generation emits patterns that are detected by legacy Gendarme as erroneous, but which are not under sufficiently fine control by the developer or cannot be annotated to suppress a warning.
Having resolved many issues stemming from a Cecil change to what the name and namespace properties of a nested type returned, and differences in behaviour under .netstandard compared with the .net Framework, the remaining sources of test failure are compiler changes (from pre-Roslyn to now) . In particular, switch on integral values is often indistinguishable from an if or if/else if construct, so some tests have just been set to [Ignore]
The following rule suites currently have unit test failures
- Framework -- 2 failures for Stack entry analysis for which there is no evidence of them ever having worked (even if the code under test is built with a 2008-vintage C# compiler the tests fail in the same way as at net6.0)
- TestMultipleCatch()
- TestTryCatchFinally() -- works with optimized build of C# 2008 (v2.0.50727\csc.exe /o)
- Smells -- 2 failures and 1 ignored due to IL changes
- false positive in
SuccessOnNonDuplicatedCodeIntoForeachLoopTest(no evidence of ever passing : still fails when the code under test was compiled with a 2008 vintage compiler with or without optimization) - false positive in
SuccessOnNonDuplicatedInSwitchLoadingByFieldsTest(Roslyn inducedswitchstatement changes -- the test succeeds with the same code built with a 2008 vintage compiler) AvoidSwitchStatementsRuletest,FailOnMethodWithSwitchTesthas similarly been[Ignore]d because of the major IL changes involved (now decompiles to anifexpression).
- false positive in
For the moment this seems to suffice to tame unreasonable, or unfixable generated, issues --
- Fix
AvoidMultidimensionalIndexerRulefor F# generated parameterless methods calledget_Item - Fix comparison of nested type names in parameters against supplied types
- Ignore
[CompilerGenerated]methods forAvoidSwitchStatementsRuleandCheckParametersNullityInVisibleMethodsRule - Ignore
[CompilerGenerated]fields and methods forVariableNamesShouldNotMatchFieldNamesRule - Ignore
<StartupCode$names inUseCorrectCasingRule - Ignore generated types containg
@in their names forAvoidUnsealedUninheritedInternalTypesRule - Ignore the
Tagsgenerated type inside union types forAvoidVisibleConstantFieldRule - Explicitly exempt the
get_andset_prefixes of getter and setter methods from theAvoidNonAlphanumericIdentifierRulesince that was not already a thing. - Make
AvoidUnneededUnboxingRulenot applicable to[CompilerGenerated]functions (e.g. Union caseCompareTo) - Exempt debugger-related generated types related to union types from
AvoidUnsealedUninheritedInternalTypeRuleandUseCorrectCasingRule - Exempt getter and setter methods from
ConsiderConvertingMethodToPropertyRule(well, duh!) - Exempt types with only fully
[CompilerGenerated]EqualsandCompareTomethods fromImplementIComparableCorrectlyRule; also explicitly exempt record types - Even if a union type is
[Obsolete]don't bother telling us its cases and case constructors depend on it. - In F# code (type with F#
[CompilationMapping]attribute) then allow single lower-case letter generic types - Exempt methods of F# generated types with
@in the name fromParameterNamesShouldMatchOverriddenMethodRule - Exempt match on union types from
AvoidSwitchStatementsRule - Exempt F# generated types with
@in the name fromUseCorrectPrefixRule,VariableNamesShouldNotMatchFieldNamesRuleandUseCorrectCasingRule - Exempt generated abstract closure types from
AbstractTypesShouldNotHavePublicConstructorsRule - Exempt constructors of record types, or generated types with
@in the name, fromAvoidLongParameterListsRule - Exempt generated types with
@in their names fromAvoidUnnecessarySpecializationRule,AvoidSpeculativeGeneralityRuleandMethodCanBeMadeStaticRule - Exempt F# placeholder arguments
_(compiled to_arg...) fromUseCorrectCasingRule - Exempt module-bound functions from
ConsiderConvertingMethodToPropertyRule - Exempt fields and constructors of records from
RemoveDependenceOnObsoleteCodeRule; accessors will still be caught but can be[SuppressMessage]d as needed - Take account of F#'s habit of making a virtual call to the base type constructor in object types constructors.
- Exempt F# code in modules, or where a
matchcould equally be aniffromAvoidSwitchStatementsRule,matchbeing idiomatic and occasionally just happening to be on an explicit integral type - Exempt property backing fields for code like
member val LocalSource = false with get, setfromAvoidUnneededFieldInitializationRule - Exempt union cases (unsealed but not likely to be inherited) from
AvoidUnsealedUninheritedInternalTypeRule - Exempt generated types with "@" in the name from
AvoidMethodWithUnusedGenericTypeRule - Exempt the field accessors of F# types (usually bypassed by the compiler by code that goes direct to the backing field) from
AvoidUncalledPrivateCodeRule - Allow for F# extension properties (
TypeName.get_...andTypeName.set_...) and for generated parameter names of the form_arg...inAvoidNonAlphanumericIdentifierRule - Consider F# extension methods/properties to be object-bound rather than module bound for
UseCorrectCasingRule - Add a heuristic to recognise F# compiler generated disposal after a
useinEnsureLocalDisposalRule - Add a
RelaxedAvoidCodeDuplicatedInSameClassRule, which looks for patterns aligning with visible sequence points, and excludes patterns containingthrow new ArgumentNullException(...) - For
AvoidLargeClassesRule, ignoreFSharpFuncandFSharpTypeFuncvalued fields in generated types with@in their names; treat them as methods in the type instead. - For
AvoidDeepNamespaceHierarchyRule, ignore F# generated namespaces of the form<StartupCode$a-b-c-d>.$.NETFramework,Version=... - For
AvoidRepetitiveCastsRule, ignore F#isthenasof anonymous temporaries (often happens inmatchexpressions on sum types) - Adapt
UseCorrectCasingRuleto be compatible withFSharpLintfor F# code (Non-class, non-public, functions should be camel-cased) - Add a
RelaxedMarkAllNonSerializableFieldsRulewhich ignores F# types with@in the name, keeping the full-strength version for cases where serializing a closure is intentional. - Skip types called
<PrivateImplementationDetails> - Don't apply
ParameterNamesShouldMatchOverridenMethodRuleto cases where the base method has a null or empty parameter name (e.g. F# interfaces) - Don't apply
DoNotDeclareVirtualMethodsInSealedTypeRuleto F# closure types - Don't apply
PreferStringComparisonOverrideRuleto generated code - Don't apply
Gendarme.Rules.Gendarme.UseCorrectSuffixRuleto types in namespaces beginning "<StartupCode$"
| Build | GitHub |
|
| Coverage | Coveralls |
Assumes net8.0/VS2022 build environment
dotnet tool restoredotnet run --project ./Build/Setup.fsprojdotnet run --project ./Build/Build.fsproj
The build stage can be done in Visual Studio with the Debug configuration to run the unit tests
- Coveralls for allowing free services for Open Source projects