Giter Site home page Giter Site logo

uber / neal Goto Github PK

View Code? Open in Web Editor NEW
424.0 15.0 28.0 1.8 MB

๐Ÿ”Ž๐Ÿž A language-agnostic linting platform

Home Page: https://uber.github.io/NEAL/

License: MIT License

Makefile 1.25% OCaml 94.29% Python 1.77% Swift 2.68% Starlark 0.02%
swift python uber ocaml

neal's Introduction

NEAL

NEAL (Not Exactly A Linter) is a language-independent code analysis tool that aims to enable more people to write quality enforcement rules.

Example

One of the simplest rules we have at Uber is to restrict the use of Forced-Values in Swift. (This post explains some of the risks of abusing forced-values.)

A forced-value consists of any expression that results in an optional value followed by the forced-unwrapping operator (!). Here's a contrived example that would result in a runtime crash.

// test.swift
(nil as Int?)!

NEAL doesn't have any rules built-in, so we have to write a new rule to detect this pattern:

// test.rules
rule NoForcedValues {
  Swift::ForcedValueExpression {
    fail("No forced unwrapping allowed")
  }
}

Now if you run NEAL you should see something like the following:

$ neal --rules test.rules test.swift

[1 of 1]: Analysing test.swift
On file test.swift: (NoForcedValues)

  1 | (nil as Int?)!
  ~ |              ^

error: No forced unwrapping allowed

Alternatively, you can create a minimal configuration file called neal.json:

{
  "rules": [ "test.rules" ]
}

After that you can get the same result by simply running:

$ neal .

For a more comprehensive guide to writing your own rules check out Writing a new rule.

Installation

The recommended way of installing NEAL is through Homebrew, using the following command:

$ brew install neal

Installing from source

To build and install NEAL from source, make sure you have OPAM installed and that you're using OCaml 4.13.1 or later.

$ brew install opam
$ opam init
$ opam switch 4.13.1
$ eval "$(opam config env)"

After you have setup OPAM and OCaml, you can setup, build and install NEAL using make.

$ NATIVE=1 make setup build install

Basic usage

NEAL has builtin support for Python and Swift, but it's highly extensible, and can be used with any language.

$ neal [options] [path to files or directories]

For a list of CLI options, runtime options and configuration attributes see the Configuration guide.

Acknowledgements

Getting Started <self> rules testing_rules reference configuration basics components/index.rst developing changelog

neal's People

Contributors

kaar3k avatar lutzifer avatar mkr-plse avatar myhrvold avatar rvantonder avatar ryanaveo avatar sm11963 avatar tadeuzagallo avatar tapthaker 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  avatar  avatar

Watchers

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

neal's Issues

Make top-level matcher optional

Right now, the grammar requires that you have a rule, and within a rule you can have multiple provider_matchers. A provider matcher is a combination of the provider to be used and a first matcher, e.g.

rule "example" {
  Swift::ClassDeclaration {
     // ...
  }
}

However, this doesn't allow matching multiple top-level siblings, e.g. in #9 where a rule is needed to match both a top-level protocol and a top-level class.

I think the most straight forward solution is to make the top level matcher optional, e.g.

rule "example" {
  Swift {
    var top_level_var := false

    ClassDeclaration {
      // ...
    }

    ProtocolDeclaration {
      // ...
    }
  }
}

Documentation updates and related feedback

I tried NEAL today and in short time I had a "working" rule. I wanted to provide some feedback on the issues I hit

  1. The readme talks about a rules config file, but doesn't mention a file name. I searched the repo and saw a mention of .rules so I tried that, but it didn't work. At first I thought NEAL was crazy fast but it turns out it just wasn't running any rules.
  2. There is no documentation on > and >> which I found info on through the source and commits. There's one example that uses them, and not many tests that use them.
  3. There is no documentation on var and condition which I noticed in a test, and eventually found more info on in #9.
  4. I was also looking for how to do cross tree sibling/cousin matches, and I think I now have something working, but there was a bunch of trial and error.
  5. I could not get match() to work, and I wasn't able to debug my use of it since I didn't know what the (generated) string it was matching against would look like.
  6. In the examples/tests, I saw one or two uses of property.access syntax, but it's not documented so I'm not clear what it's used for.
  7. I'm getting a number of swift parsing errors, but it doesn't tell me which syntax NEAL is failing on and the error message is funky: SyntaxError}: satisfy.
  8. As a beginner, it's not clear how to mix where clauses with properties that are lists of nodes, or conversely, how properties mix with matchers.
  9. When there is a syntax error in a .rules file, only a line and column number are shown. It would be nice to show the line and a ^ label just like how warnings/errors in source files are shown.
  10. Wish: Print the number of warnings/errors at the end of processing. Even better if it listed counts by rule name.

Better string lexing

The lexer should:

  • Support empty strings
  • Support basic escaped characters (specially \")

Feature request: Child Combinator

I'd like to apply a rule only to ConstantDeclarations which are class properties.

I'd hoped I could achieve this by nesting the ConstantDeclaration directly within the ClassDeclaration (as below) but this matches all ConstantDeclarations within the class (as it should!).

rule NoSelfCapturingConstantDeclaration {
  Swift::ClassDeclaration {
    ConstantDeclaration {

This is a feature request for a child combinator, to allow me to target only direct decedents of a ClassDeclaration. (Perhaps in the style of the CSS child selector https://developer.mozilla.org/en-US/docs/Web/CSS/Child_selectors)

Incorrectly labeling of ImplicitlyUnwrappedOptional

The following code and rule incorrectly result in a NEAL failure.

public class MyObject {
    public func myFunc(_ object: Any) -> Bool {
        return object as? Dictionary<AnyHashable, Any> != nil
    }
}
rule Bang {
  Swift::TypeIdentifier where ImplicitlyUnwrappedOptional == true {
    fail("No forced unwrapping allowed")
  }
}

Since there is no force unwrap the test should not fail.

When parens are added the rule no longer results in a NEAL failure.

public class MyObject {
    public func myFunc(_ object: Any) -> Bool {
        return (object as? Dictionary<AnyHashable, Any>) != nil
    }
}

Example of how to write a rule with sibling nodes

My use case is I want to warn when a Swift ImportDeclaration with a particular identifier is in a file that also contains a class that conforms to a particular protocol. I'm not sure how to declare that this rule should match an ImportDeclaration and ClassDeclaration that are siblings. I've tried a few things and none of them seem to work (syntax errors). Can you clarify if this is currently possible and maybe provide a small example? I could try to submit a PR with some docs if I got that far.

I see Add support for sibling patterns through conditional variables in the changelog and the closest I can find to something about this is this in a rule. It probably doesn't help that I'm not familiar with OCaml either ๐Ÿ˜„

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.