GithubHelp home page GithubHelp logo

flinter's People

Contributors

cmeeren avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

flinter's Issues

Rule ideas

This issue serves as notes for rule ideas.

In general

  • Rules should not be based on personal opinion. They should be based on (and point to) official recommendations or other well-reasoned/researched sources. If you want a rule that is not covered there, try to get it added to official coding guidelines first.
  • Rules should be sufficiently generally applicable that they can be enabled by default.
  • Configuration options should be minimal. Like Fantomas, Flinter should do "the right thing" out of the box and push toward uniform(ly good) F# code. Flinter should improve the F# ecosystem; that is harder to do when everyone can configure everything.

Out of scope

  • Code formatting (handled by Fantomas)
  • Anything that is better handled by the compiler itself (raise an issue there instead)

Ideas for categories (not complete)

  • Complexity (e.g., cyclomatic complexity, function/method size, number of activated entities)
  • Consistency (e.g., naming)
  • Correctness (e.g., missing use, recursive async CE via do! instead of return!, recursive task CE, missing tail recursion (or is that not generally useful?))
  • Performance (e.g., use struct single-case DUs)
  • Redundancy (e.g., dot in index notation, named unit values, string applied to string argument, etc.)
  • Robustness
  • Security
  • Some categories here
  • More categories here

Rule ideas

  • Quick-notes, to be elaborated/merged into the below:

    • let/let! instead of use/use! on IDisposable and IAsyncDisposable (for bang operators, depending on the CE, these may be wrapped in arbitrary container types like Async or Task)
    • Dead code
    • Ideas from:
    • Naming
    • Unneeded type annotations (in internal/private code) - can this be detected?
    • Using prefix notation for generics (Foo<'a>) except for option, voption, list, [], ref (source: formatting guidelines)
    • Named parameters or values that are typed to unit (e.g. Option.defaultWith (fun thisIsUnit -> ...))
    • Usage of Option.Value, List.Head and similar?
    • Recommend Sealed and AbstractClass on types with only static members?
    • Don't catch an exception and throw from the with expression without the thrown exception containing the caught exception (loses stack trace and other information)
    • Use string interpolation instead of sprintf, failwithf etc. (when not piped)
  • Guidelines from the book "Code that fits in your head" by Mark Seemann

    • Limit number of "activated objects" in any given function (section 7.2.7 of the book)

      • A method should interact with a maximum number of local variables, method parameters, and class fields.
      • As a rule of thumb, try to keep this number at 7 or below. Keeping it low ensures that the method implementation fits in your head.
      • What about nested functions? There's no single-level "Class -> Method" structure; functions can have arbitrarily nested inner functions (often closing over some values from the outer scope).
    • Cyclomatic complexity (section 7.1.2 of the book)

      • It starts at 1 and increases by 1 for each branch. This includes not only if/elseif/else, but also ternary operators, null coalescing operators, pattern match cases, loop bodies, etc.
        • Counting all pattern match cases would make it impossible to avoid this rule. A single function with a top-level pattern match against an enum with too many cases would trigger this rule, and nothing sensible could be done about it.
      • As a rule of thumb, try to keep this number at 7 or below. Keeping it low ensures that the method implementation fits in your head.
    • Parse, don't validate (section section 7.2.5 of the book)

      • If you just validate a string (or any other type, for that matter) and then pass that string further into your code, other parts of the code have no guarantees about what you validated, and it's hard to know at any point in the code what guarantees the string comes with. Should the deeper code validate the string again? It's impossible to know at that level; it must assume knowledge about what happens outside it.
      • Instead, when you validate data, wrap it in a type that carries the guarantees you desire. Do this as soon as possible in your code (i.e., just after receiving it from the outside world), and then only work with the custom type inside your code.
        As a simple example, if you receive an email address via a string in an API, once you have validated that the string is a valid email address, wrap the string in a custom EmailAddress type and use that everywhere else. All code that uses EmailAddress knows that they have a valid email address, as opposed to if they had just worked with a plain string.
      • How to implement this? E.g. if passing a primitive like a string into a function, passing it to a predicate (a bool-returning function), and then returning the string (unmodified). Might be complicated.
  • Guidelines from F# coding conventions

  • Guidelines from F# component design guidelines

  • Stuff from .NET framework design guidelines

    • Naming guidelines - common capitalization mistakes

      • But verify each one; some of them seem wrong, like userName (username seems to be a closed-form compound word)
    • Names of common types

      • Attribute suffix for types inheriting from System.Attribute
      • EventArgs suffix for types inheriting from System.EventArgs
      • Do not suffix enum types with Enum, Flag, or Flags
      • Dictionary suffix for types implementing IDictionary
      • Collection suffix for types implementing IEnumerable, ICollection, or IList
      • Stream suffix for types inheriting from System.IO.Stream
      • Use singular type names for enumerations that are not bit fields (flags), see System.FlagsAttribute
      • Use plural type names for enumerations that are bit fields (flags), see System.FlagsAttribute
    • Member names

      • Do not have a property that when prefixed with Get has the same name as a method. This pattern typically indicates that the property should really be a method. (What about Set?)
      • DO name collection properties with a plural phrase describing the items in the collection instead of using a singular phrase followed by "List" or "Collection".
      • Events should not be prefixed or suffixed with Before or After
    • Parameter names

      • DO use left and right for binary operator overload parameter names if there is no meaning to the parameters.
        • Is this applicable to F#?
      • DO use value for unary operator overload parameter names if there is no meaning to the parameters.
        • Is this applicable to F#?
    • Interface design

      • AVOID using marker interfaces (interfaces with no members). Use an attribute instead.
      • DO provide at least one type that is an implementation of an interface.
      • DO provide at least one API that consumes each interface you define (a method taking the interface as a parameter or a property typed as the interface).
    • Struct design

      • DO NOT provide a parameterless constructor for a struct.
      • DO NOT define mutable value types.
      • DO implement IEquatable<T> on value types.
    • Enum design

      • DO use powers of two for the flag enum values so they can be freely combined using the bitwise OR operation. (See System.FlagsAttribute)
        • Also do the reverse check, by detecting enums with powers-of-two values that do not have the attribute?
        • What about this? "CONSIDER providing special enum values for commonly used combinations of flags."
      • DO name the zero value of flag enums None. For a flag enum, the value must always mean "all flags are cleared."
    • Property design

      • DO NOT provide set-only properties or properties with the setter having broader accessibility than the getter.
      • AVOID throwing exceptions from property getters.
      • AVOID indexers with parameter types other than Int32, Int64, String, Object, or an enum.
    • Event design (low priority?)

    • Field design

      • DO NOT provide instance fields that are public or protected.
    • Parameter design

      • Avoid out/ref parameters
    • Exception throwing

      • DO NOT have public members that return exceptions as the return value or an out parameter.
      • AVOID explicitly throwing exceptions from finally blocks.
    • **Using standard exception types

      • Don't throw Exception (failwith/failwithf) or SystemException; use a more specific exception
        • But does this really matter if it's an exception that should never occur? For example, Felicity throws many exceptions from its internals that are never intended to be caught, just to supply a helpful error message.
      • DO NOT catch System.Exception or System.SystemException in framework code, unless you intend to rethrow.
      • AVOID catching System.Exception or System.SystemException, except in top-level exception handlers.
      • DO NOT throw or derive from ApplicationException.
      • DO NOT allow publicly callable APIs to explicitly or implicitly throw NullReferenceException, AccessViolationException, or IndexOutOfRangeException. These exceptions are reserved and thrown by the execution engine and in most cases indicate a bug.
      • DO NOT explicitly throw StackOverflowException
      • DO NOT catch StackOverflowException.
      • DO NOT explicitly throw OutOfMemoryException
      • DO NOT explicitly throw COMException, ExecutionEngineException, and SEHException`
    • Usage guidelines: Arrays

      • DO prefer using collections over arrays in public APIs
      • DO NOT use read-only array fields
        • This is a subset of "do not set read-only fields to mutable types" mentioned elsewhere in the framework design guidelines
    • Usage guidelines: Attributes

    • Usage guidelines: Collections

  • Officially documented best practices:

  • Check existing linter rules for ideas (note: Before implementation, they should be grounded in something more than just "implemented in another linter")

    • FSharpLint

      • Recursive async functions should end with return! not do!
      • Redundant new keyword?
      • failwith, raise, nulArg, invalidOp being passed more than one argument
      • invalidArg being passed more than two arguments
      • failwithf being passed more arguments than contained in the format string
      • All the "max lines in ..." and "max number of ...": Are these all better covered by "cyclomatic complexity" and "max number of activated objects" (from "Code that fits in your head")?
      • Simplify lambda (only if lambda re-implements function, no partial application? There are some important false positives here; see own reported issues in JetBrains issue tracker for examples)
      • Replace lambda with piping/composition (see caveats above)
      • Favor ignore over let _
      • Replace match clause _ as x with x
      • Replace wildcard tuples (_, _) with wildcard _
      • Simplified custom hint syntax? Use this internally as an implementation detail, too?
      • Avoid partial functions like List.find (but what it you know it will exist? Pointless to use pattern matching only to have the None case be failwith "Unreachable code")
      • Favor typed ignore
      • Use reraise () instead of raise inside a try ... with block (not applicable inside a CE)
      • Consistent this identifier in classes/interfaces (is this covered by the F# style guide?)
    • Various Roslyn analyzers

TODO: For all rules that work with accessibility modifiers, what if signatures files are used?

Dev notes

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.