GithubHelp home page GithubHelp logo

ocaml-ppx / ppxlib Goto Github PK

View Code? Open in Web Editor NEW
242.0 242.0 89.0 10.24 MB

Base library and tools for ppx rewriters

License: MIT License

Makefile 0.03% OCaml 97.20% Shell 0.06% Perl 1.18% Python 0.14% Turing 1.38%

ppxlib's People

Contributors

aalekseyev avatar antalsz avatar arozovyk avatar burnleydev1 avatar c-cube avatar ceastlund avatar davesnx avatar dianaoigo avatar ejmg avatar gridbugs avatar hhugo avatar jeremiedimino avatar jonahbeckford avatar kakadu avatar kit-ty-kate avatar marrious11 avatar matthewelse avatar nathanreb avatar ncik-roberts avatar nicovank avatar panglesd avatar pitag-ha avatar rgrinberg avatar sim642 avatar smuenzel avatar tatchi avatar tmattio avatar tmcgilchrist avatar trefis avatar xclerc 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  avatar

ppxlib's Issues

Restore ocamlbuild plugin?

I've been having to maintain a pin of ppxlib that restores the ocamlbuild plugin, for compatibility with BinaryAnalysisPlatform/bap#820. Since the changes consist literally of nothing but reverting the deletion of files in the ocamlbuild_plugin/ directory, I would like to request that this support be restored. I can make a PR easily.

Otherwise, we will need to vendor a separate version of ppxlib somewhere, which will be a lot of trouble since we will need to change the name etc. to not conflict with other installations of ppxlib (unless there is some easy way of simply splitting the plugin into a separate, optional package).

No support for Parsetree "case" Context.t

Hi, I’m trying to write a PPX extension in which the extension point is used in place of a "case" statement. Usage would look like:

match x with
| 1 -> 1
| [%myExt pattern TO expression]
| _ -> 3

A "case" node produced by Parsetree has the form:

type case = {
| pc_lhs : pattern;
| pc_guard : expression option;
| pc_rhs : expression;
}

When creating an Extension.t, Extension.declare requires a Context.t as an argument. However, there is no Context.t for type "case".

Is there any lower-level way I can register my transformation to the Driver without using declare or do I require official support for a Context.case?

Export `Ast_pattern.drop` in module interface

ast_pattern.ml defines a drop pattern, which is used to match against any pattern and then discard it without capturing. Consider the following example: say I want to capture and do something to a let .. = .. declaration. I would make a pstr_value pattern as follows:

val pstr_value : (rec_flag, 'a, 'b) t -> (value_binding list, 'b, 'c) t -> (structure_item, 'a, 'c) t
pstr_value __ __

Where the first __ captures the recursive/non-recursive flag, and the second __ captures the actual .. = .. pairs. I would define a continuation to handle the captured values as such:

fun rec_flag bindings ->
  bindings
  |> List.map ..

But I'm not interested in the rec_flag at all! I would much rather write

fun bindings ->
  bindings
  |> List.map ..

rather than have to prepend a _ parameter, or have to change my handling function's signature every time the pattern is changed--for instance, if I have a function that transforms value_bindings in different contexts, and in some contexts, the pattern capturing the value_binding comes with an extra parameter, then instead of using the same handler function for both cases, I have to pass one of them through an extra map first if I want to reuse the same handler. It would be much more convenient if I could write something like

pstr_value drop __

or, if one wants to get creative with symbols,

pstr_value ___ __

so that the value matched by the ___/drop is effectively an "any" match without actually capturing the value. It would be somewhat similar to the ptyp_any pattern, which captures any type expression, but instead something like pany_any.

Context dependent extensions

I'm currently trying to write a ppx extension that would be rewritten to the fully qualified path of the value it appears in (see https://github.com/NathanReb/ppx_there).

It seems that the right way to do that currently is to use Ppxlib.Driver.register_transformation ~impl:my_expand_function because I need some context to actually rewrite the extension node.

I looked into Extension though and found out about Extension.Expert. I'm not quite sure I got this right but I have the impression that for now the only thing it allows me to do is to verify the payload fits the declared Ast_pattern.t and if so get the payload node.

I was wondering if there were more helpers for such ppx plugins provided by ppxlib as I feel like I'm gonna redo a lot of the work that's already done here.

Remove `Context_free.Rule.attr*`

These are only used for [@@deriving ...] and it's unlikely they'll ever be used for something else. Now that [@@deriving] is part of ppxlib, I think removing them and specializing the implementation to only support [@@deriving ...] would make the code simpler.

Metaocaml attributes

Recent versions of metaocaml mark quotations and splicing using ppx attributes.
For example .< e >. is desugared at parsing time in e [@metaocaml.bracket].

ppxlib doesn't seem to recognize them, and complain loudly about them being unused. It would be nice to recognize them, and let them pass through to the (modified) typechecker.

I got the issue with ppx_sexp_conv, but I figured this is the right repository for this.

Inconsistent API for `Ast_builder.pconstruct`, `econstruct`

The other Ast_builder.p* and Ast_builder.e* functions, such as estring and eint and pvar, etc., take in "simple" values, making it easier to construct common patterns/expressions without too much verbosity. For instance, to create the string expression "hello", we can do

val estring : loc:Location.t -> string -> expression
estring ~loc:Location.none "hello"

significantly simplifies

{ pexp_desc = Pexp_constant (Pconst_string ("hello", None))
; pexp_loc = Location.none
; pexp_loc_stack = []
; pexp_attributes = []
}

as well as the `Ast_builder` direct equivalent

```ocaml
pexp_constant ~loc:Location.none (Pconst_string ("hello", None))

It is very nice.


Meanwhile, we have pconstruct (resp. econstruct), which, for some reason, takes in a whole constructor declaration, completely defeating the purpose of having a pconstruct function. As an example, create a Some x constructor pattern, the shortest way to call the current pconstruct function is

val pconstruct : constructor_declaration -> pattern option -> pattern
pconstruct
  (constructor_declaration
    ~name:{ txt = "Some"; loc = Location.none }
    ~args:[]
    ~res:None
  )
  (pvar "x")

In fact, _simply calling the explicit ppat_construct function is much more concise:

ppat_construct 
  { txt = "Some"; loc = Location.none }
  (pvar "x")

Here's a few reasons why this current behavior is not good:

  • It completely defeats the purpose of having a pconstruct function, since the pconstruct call is twice as long as the direct AST construction via ppat_construct!

  • Bad semantics: the ~args and ~res properties of the constructor declaration is irrelevant when constructing the match-pattern--it's simply not the right type to use! The source for Ast_builder confirms that pconstruct only uses name and loc of the constructor:

    let pconstruct cd arg = ppat_construct ~loc:cd.pcd_loc (Located.map_lident cd.pcd_name) arg

    hence semantically, it doesn't make sense to take in a whole constructor_declaration.

    Another semantic error here is that constructor-patterns take Longident.t identifier values, such as Http.Request_kind, but constructor-declarations are only supposed to take strings without dots, i.e. Request_kind. One can simply use a string with a . in it and pass that to constructor_declaration, but that again is not a good semantic; furthermore, the current implementation does not parse strings the way evar does, so having strings with . instead of using Longident.t actually can break things in certain cases.

  • It uses inconsistent API conventions. All the other functions in Ast_builder.Default are called with a leading ~loc argument to specify the location, but pconstruct doesn't but instead steals it from the constructor_declaration implicitly via .pcd_loc. That not only means sometimes using the incorrect location (if a PPX parses a constructor declaration, stores it, and builds a constructor pattern later, the location referenced should be that of the pattern-matching construct, not the original declaration) but is also just inconsistent API.


The proposal: replace the current interface/implementation with

val pconstruct : loc:Location.t -> string -> pattern option -> pattern
let pconstruct ~loc name arg =
  ppat_construct 
    ~loc 
    { txt = Longident.parse name; loc }
    arg

Which would be used like

(pconstruct "Some" @@ Some (pvar "x"))

Which would be much nicer to use, and matches the other p* functions. Something similar would be done to econstruct.

[Feature] Local rewriting rule schema for attributes

Could a schema for attributes more generic than the attr_* rules be added to the local rewriting rules, with an API similar to that for extensions? For example the [@JSX] attributes the Reason parser attaches to expressions it parsed as JSX are supposed to be used to be rewritten by custom ppx, so it would be nice to be able to define a rule to match expression nodes that have that attribute.

Extract pieces of Deriving.t

If I have a value of type Ppxlib.Deriving.t, can I extract the pieces used to create it, in particular, the str_type_decl?

If I can't pull out that piece directly, is there some other way to apply the function embedded in the deriver.

I've looked at the types and documentation, and don't see how to proceed.

optional parameter for ppx_bin_prot

ppx_deriving supports an optional flag. From their docs:

It's possible to make deriving ignore a missing plugin rather than raising an error by passing an optional = true option, for example, to enable conditional compilation:

type addr = string * int
[@@deriving yojson { optional = true }]

This works well for their builtins, but doesn't seem to work for bin_io - which is making writing platform independent code difficult. The bin_io folks suggested that this would need to be implemented here.

Is this the place to support optional for all ppx derivers?

"dune runtest" for ppxlib-0.12.0 fail when build by ocaml-4.10.0

build environment was installed via "opam install ppxlib"

$ ocaml --version
The OCaml toplevel, version 4.10.0

ppxlib-0.12.0 $ dune runtest
expect_test alias test/base/runtest (got signal ABRT)
(cd _build/default && test/expect/expect_test.exe test/base/test.ml &> /dev/null
expect_test alias test/code_path/runtest (got signal ABRT)
(cd _build/default && test/expect/expect_test.exe test/code_path/test.ml &> /dev/null
expect_test alias test/deriving/runtest (got signal ABRT)
(cd _build/default && test/expect/expect_test.exe test/deriving/test.ml &> /dev/null
expect_test alias test/driver/attributes/runtest (got signal ABRT)
(cd _build/default && test/expect/expect_test.exe test/driver/attributes/test.ml &> /dev/null
expect_test alias test/driver/non-compressible-suffix/runtest (got signal ABRT)
(cd _build/default && test/expect/expect_test.exe test/driver/non-compressible-suffix/test.ml &> /dev/null expect_test alias test/driver/transformations/runtest (got signal ABRT)
(cd _build/default && test/expect/expect_test.exe test/driver/transformations/test.ml &> /dev/null
expect_test alias test/quoter/runtest (got signal ABRT)
(cd _build/default && test/expect/expect_test.exe test/quoter/test.ml &> /dev/null
expect_test alias test/traverse/runtest (got signal ABRT)
(cd _build/default && test/expect/expect_test.exe test/traverse/test.ml &> /dev/null

default $ test/expect/expect_test.exe test/base/test.ml
Fatal error: executable program file not found
Aborted

can we use ocamlformat in ppxlib?

Ocamlformat is the default in Jane Street, but I don't know how people outside Jane Street feel about it.

Would the ppxlib maintainers entertain a pull request that ocamlformat'ed ppxlib?

Question: Running two transformation when 1st one creates input for second one

I have a transformation distrib which generates code suitable for transforming by deriving. I linked them into executable and -print-transformation switch gives both distrib and deriving.

But when I run compiled executable with two transformations only first one seems to be applied. I tried to use -apply deriving,distrib,deriving,deriving,deriving,distrib but only distrib is being applied. I tried to use standalone rewriter executable for my deriving plugin using -pp switch to pass distrib before running deriving. It worked as expected: distrib has generated code with [@@deriving...] inside and deriving have expanded everything in the end.

The question is: how exactly multiple rewriters linked together are working? From my current perspective it seems that if we have two transformations then the 2nd one is not applied to the code generated by the first one. Is it intentional?

P.S. I discovered that -no-merge leads to expected behavior.

No-arguments pattern in Ast_pattern

Hello, I am trying to write a ppx that essentially just drop-in replaces the expression [%operating_system] to a string which represents the operating system the user is running on.

In skeleton/pseudocode, this was the idea I was trying to go for:

open Ppxlib

let name = "operating_system"

let expand ~loc ~path:_ (env : string) =
  Ast_builder.Default.estring s ~loc
  
let ext =
  Extension.declare
    name
    Extension.Context.expression
    ____
    expand

let () = Ppxlib.Driver.register_transformation name ~extensions:[ext]

However, I'm not sure what to put where the ____ is if I don't require any arguments to be passed into my expression. Is there a way to do this without using more direct language hooks?

OCaml 4.11 incompatibility

Along with the Longindent.parse deprecation there is seem to be a fatal error: "OCaml and preprocessor have incompatible versions":

- 14 |   let lident x = mk (Longident.parse x)
-                           ^^^^^^^^^^^^^^^
- Alert deprecated: Longident.parse
- this function may misparse its input,
- use "Parse.longident" or "Longident.unflatten"
-     ocamlopt src/gen/.gen_ast_pattern.eobjs/native/import.{cmx,o}
- File "src/gen/import.ml", line 14, characters 21-36:
- 14 |   let lident x = mk (Longident.parse x)
-                           ^^^^^^^^^^^^^^^
- Alert deprecated: Longident.parse
- this function may misparse its input,
- use "Parse.longident" or "Longident.unflatten"
-     ocamldep traverse/.ppxlib_traverse.objs/ppxlib_traverse.pp.ml.d (exit 2)
- (cd _build/default && /home/travis/.opam/ocaml-variants.4.11.0+trunk/bin/ocamldep.opt -modules -impl traverse/ppxlib_traverse.pp.ml) > _build/default/traverse/.ppxlib_traverse.objs/ppxlib_traverse.pp.ml.d
- >> Fatal error: OCaml and preprocessor have incompatible versions
- Fatal error: exception Misc.Fatal_error
[ERROR] The compilation of ppxlib failed at
        "/home/travis/.opam/opam-init/hooks/sandbox.sh build dune build -p
        ppxlib -j 1".

See full log at https://travis-ci.org/github/ocaml-ppx/ppx_deriving/jobs/683869554#L883
Caught in ocaml-ppx/ppx_deriving#220 by @kit-ty-kate

Question about linking actual rewriter with ocamlbuild

There is a command to build custom rewriter using ocamlfind but I currently have some troubles making things work

The problem is that current approach relies on the fact that ppx plugin is built as findlib library. And if I'm developing syntax extension in my project and want to run some tests than I need to compile syntax extension and after that compile tests with adjusted OCAMLPATH variable. I tried to pass cmxa manually and remove dependency on findlib package but it seems that the order of cmxa's matters and only way to do every all right is to reference PPX package. With this approach there is also an issue with dependencies: tests should be recompiled when the package changes and this currently doesn't work as I want.

Can you give me a piece of advice without recommending switching to dune?

P.S. This is a question mentioned in #18

File "src/jbuild", line 18, characters 17-40: # Error: "ocaml-migrate-parsetree" is not a ppx driver during the opam upgrade with dune-1.0


#=== ERROR while compiling ppxlib.0.2.1 =======================================#
# context     2.0.0~rc3 | linux/x86_64 | ocaml-system.4.06.0 | file:///home/akochkov/data/tmp/opam-repository
# path        ~/.opam/default/.opam-switch/build/ppxlib.0.2.1
# command     ~/.opam/default/bin/jbuilder build -p ppxlib -j 7
# exit-code   1
# env-file    ~/.opam/log/ppxlib-630120-bf8001.env
# output-file ~/.opam/log/ppxlib-630120-bf8001.out
### output ###
# File "src/jbuild", line 18, characters 17-40:
# Error: "ocaml-migrate-parsetree" is not a ppx driver



<><> Error report <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
┌─ The following actions failed
│ λ build ppxlib 0.2.1
└─ 

[Feature] A list of generator arguments with described structure

I want to write [@@deriving foo ~name1:... ~name2:... ~name3:.... ....... ~name100500:....] where all name%d should be arbitrary. In the generation function I want to get a (Longident.t loc * expression) list with all the expressions that describe the arguments of foo.

Getting tool_name from rewriter and/or derivers

Following ocaml/dune#1861 I'm trying to add a specific behaviour when my ppxlib based deriver is used in an ocamldep context. ocaml-migrate-parsetree seems to make it available through a config argument but I can't seem to find how I could access it from ppxlib.

I worked out an ugly solution to come around that limitation for now but if I'm indeed not missing something, it might be useful to find a way to pass it on to the actual ast -> ast function.

Discussion about compatibility

This sums up a discussion we had with @hhugo this morning. There is no action to take on this ticket, I'm just leaving it here so that the plan is clear and in case others want to chime in.

The API of ppxlib is based on a selected version of the AST. This version is not fixed forever and will move from now and then. It will likely change shortly after every new release of the compiler. This means that projects using ppxlib might break every time the selected version of the AST is updated.

The planned solution is to integrate ppx_view into ppxlib for the 1.0.0 release of ppxlib. With ppx_view, we can provide a stable API to deconstruct the AST. Coupled with a stable API to construct the AST and the various traversal classes provided by ppxlib, this will allow ppx rewriters to replace some or all of their explicit references to the concrete data types of Parsetree.

Another other solution would be to do like ocaml-migrate-parsetree and ppx_tools and provide several versions of the whole ppxlib library. However, I believe that the cost will be too high in terms of both maintainability and complexity of the overall system.

Deprecation error points to "Ocamlcommon" instead of "Ocaml_common"

Hi, I'm trying to write my first ppx rewriter for Ocaml using ppxlib and when I tried to open Ast_mapper I was confronted with the error Accessing this module directly is deprecated, use Ocamlcommon.Ast_mapper instead, however it took me quite some searching to find that the module is actually called "Ocaml_common". This should be fixed.

(As an aside, a link to the most up-to-date documentation would be greatly appreciated as most of the PPX docs and tutorials are very old and inconsistent :/ )

[RFC] Integrate ppx_view

This ticket describe how to integrate ppx_view in ppxlib. This is the last major work to do before we release the 1.0.0 version of ppxlib. These two projects could stay separate, however given that using view pattern will improve a lot the ppx compatibility story, I believe that it is worth merging them to promote the use of view patterns.

The rest of this PR explains what needs to be done.

Structure of ppx_view

ppx_view is composed of:

  • ppx_view.lib, a library defining the type of view patterns + some combinators. It is used as the runtime library of the ppx rewriter
  • ppx_view, a ppx rewriter that interprets match%view ... expressions and produces code using the ppx_view.lib library
  • ppx_view.ast, a library providing view patterns for the OCaml AST. Its API is the dual of the Ast_helper module from the compiler libraries

A lot of the code of ppx_view.ast is automatically generated from parsetree.mli.

ppx_view.lib and ppx_view.ast can be integrated in ppxlib as it. Most of the work will be on integrating ppx_view.ast so that the overall API of ppxlib stays consistent and is ready for long term backward compatibility.

Ast_pattern and ppx_view.ast

ppxlib currently has a Ppxlib.Ast_pattern module that serve a purpose similar to the one of ppx_view.ast. However, the API of Ppxlib.Ast_pattern follows the naming convention of Ppxlib.Ast_builder rather than Ast_helper.

It doesn't make sense to maintain both Ppxlib.Ast_pattern and the original ppx_view.ast. View patterns are better than what Ppxlib.Ast_pattern currently provides, so we should migrate Ast_pattern to use plain view patterns instead. However, to make it easier to upgrade code currently using Ppxlib.Ast_pattern, we should keep the current naming conventions of Ast_pattern.

Most of the combinators of Ast_pattern are generated, so it should be easy to adapt the generator to generate view patterns.

Backward compatibility story

As the OCaml AST evolves, we will update the API of Ast_builder and Ast_pattern in backward compatible ways. For instance by adding new foo_v409 functions, or by clever use of optional arguments. One consequence is that we can no longer generate the interface of Ast_builder and Ast_pattern. Instead, we should just infer the mli of Ast_pattern_generated and include it in Ast_pattern and do the same for Ast_builder. The implementation can still be generated, and we will overload a few functions in Ast_pattern to make sure to preserve backward compatibility when the signature of the generated functions change.

While this will work fine, over time the API will become quite ugly and we will want to clean it up. For this reason, we should version the whole API of Ast_builder and Ast_pattern. I.e. Ast_builder will become Ast_builder.V1 and Ast_pattern will become Ast_pattern.V1. When the API will become too messy, we will mint V2 versions.

Note: to make things simpler, we can get rid of Ast_builder.Make. This function is almost never used and it is more code to maintain.

Longident.parse is deprecated

Fedora Rawhide currently has a prerelease of OCaml 4.11. Building ppxlib now fails like so:

File "src/gen/import.ml", line 14, characters 21-36:
14 |   let lident x = mk (Longident.parse x)
                          ^^^^^^^^^^^^^^^
Error (alert deprecated): Longident.parse
this function may misparse its input,
use "Parse.longident" or "Longident.unflatten"

What is the right thing to do? Should I disable the alert and leave the code as is? Would it be correct to replace (Longident.parse x) with (Parse.longident (Lexing.from_string x))?

Read the docs: empty module index and ads

The current documentation page for ppxlib (March 2019) has two aspects that are worth an issue:

  • Many of the content seems missing. On my machine the "Index" page is empty and the "Module Index" page appears to show a platform-specific missing-page content.

  • There are ads now in the left column. (In my case a message about the fact that I have an ad-blocker, but should support them by watching their "Ethical ads".)

The second point is I think worth thinking about. Yes, ReadtheDocs is a cool platform that lowers barrier to entry by being visually appealing and being similar to the documentation of other famous/popular systems, but is it worth showing ads in OCaml project documentations? Maybe you should consider keeping the exact same style but migrating to dedicated hosting space, without the ads. I think this shows that centralizing documentation in one third-party website is often not, in the long run, the best approach.

Pass attribute location to Deriving.Generator

Currenlty it seems that the location passed to the generator function

loc: Location.t -> path: string ->  `input_ast -> `output_ast

at least in the str_type_decl case is the loc for the whole Pstr_type node. This is a bit annoying because if you then use that loc for the generated nodes (which I assume is what most ppxlib users do) that means that any error in there will be reported on the whole type declaration. That can be a bit confusing to the end user of the deriving plugin.

What I'd like to be able to do instead is locate it on the attribute node for [@@deriving ...] which I believe would make more sense. It might even be worth locating it on the right part of the attribute payload, ie the one corresponding to the registered deriver.

I don't think there's a nice way, if there's even one, to get that location from the input ast nodes.

Would you consider changing what is passed as loc or at least adding a separate argument for the different relevant locations?

OCaml 4.08 and Parsetree

In ppxlib 0.8.0, the file ast/import.ml contains the line:

module Js    = Migrate_parsetree.OCaml_407

When building in an OCaml 4.08 setup, that seems to make Ppxlib.Parsetree refer to the Parsetree in Ast_407 from ocaml-migrate-parsetree.

That seems wrong, and makes code fail to compile that compiled fine with OCaml 4.07.1.

Shouldn't that line use the current version of OCaml, rather than a fixed version?

ppxlib v0.8.0 and v0.7.0- pretty print ASTs as code differently on ocaml >= 4.05

I recently ran into this. It's not a major issue and although I know I shouldn't rely too much on the pretty printed ASTs, I do for testing which makes this a bit annoying.

For example, in ppx_yojson tests I diff the pretty printed post rewriting AST for the following piece of code:

let null : json = [%yojson None]

which, until now, was:

let null : json = `Null

for ocaml 4.04 to 4.07.

With ppxlib.0.8.0, it is printed as:

let null = (`Null : json)

for ocaml 4.04 and 4.05 but still printed as above for ocaml 4.06 or higher.

Do you have any idea what lead to that change and if there's a way to fix it? Once again it's nothing major but if it's easy/quick enough I'll gladly take care of it!

I'm also curious how to use the -styler option of the standalone driver as it could offer me a way to work around that.

ppxlib.traverse mishandles types named with module prefixes

Would it be possible to extend the ppx s.t. it would include the
module prefixes into its method names via some kind of mangling
scheme? E.g., type Foo.Bar.baz goes to foo__bar__baz instead of to
baz.

(* This is correct. *)
type direct = Direct of int * string [@@deriving_inline traverse_iter]

let _ = fun (_ : direct) -> ()

class virtual iter =
  object (self)
    method virtual int : int -> unit

    method virtual string : string -> unit

    method direct : direct -> unit =
      fun x ->
      match x with
      | Direct (a, b) ->
        self#int a;
        self#string b
  end

[@@@end]

(* This generates incorrect code. *)
type with_module_prefixes = With_module_prefixes of Int.t * String.t
[@@deriving_inline traverse_iter]

let _ = fun (_ : with_module_prefixes) -> ()

class virtual iter =
  object (self)
    method virtual t : t -> unit

    method virtual t : t -> unit

    method with_module_prefixes : with_module_prefixes -> unit =
      fun x ->
      match x with
      | With_module_prefixes (a, b) ->
        self#t a;
        self#t b
  end

[@@@end]

(* The workaround is to alias the types so I can refer to them without
module prefixes. *)
type int__t = Int.t
type string__t = String.t

type with_aliases = With_aliases of int__t * string__t
[@@deriving_inline traverse_iter]

let _ = fun (_ : with_aliases) -> ()

class virtual iter =
  object (self)
    method virtual int__t : int__t -> unit

    method virtual string__t : string__t -> unit

    method with_aliases : with_aliases -> unit =
      fun x ->
      match x with
      | With_aliases (a, b) ->
        self#int__t a;
        self#string__t b
  end

[@@@end]

Deriving abstract types

Hello everyone,

I have recently started doing my first ppx and I encountered a problem:
My goal was to generate functions for a record which I would keep as an abstract type in the signature of my module.
The structure part was working fine but when doing the signature part, I met one problem:
My type being an abstract one, I could not access my record fields anymore, therefore I could not generate the functions signatures.

This is why I was wondering if there was a solution to allow me to have my record information available for the ppx but remove them with the abstraction of my type right after.

What do you think of it ?

Appveyor fails on Sys_error("C:\cygwin64\tmp\cinapsf14ff5.mli: Permission denied")

+ C:\cygwin64\home\appveyor\.opam\ocaml-variants.4.07.1+mingw32c\bin\dune.exe "runtest" "-p" "ppxlib" "-j" "2" (CWD=C:/cygwin64/home/appveyor/.opam/ocaml-variants.4.07.1+mingw32c/.opam-switch/build/ppxlib.0.9.0)
-       cinaps alias src/runtest (exit 2)
- (cd _build/default/src && .cinaps/cinaps.exe -diff-cmd -)
- Fatal error: exception Sys_error("C:\cygwin64\tmp\cinapsf14ff5.mli: Permission denied")
-       cinaps alias src/runtest (exit 2)
- (cd _build/default/src && .cinaps/cinaps.exe -diff-cmd -)
- Fatal error: exception Sys_error("C:\cygwin64\tmp\cinapsf14ff5.mli: Permission denied")
[ERROR] The compilation of ppxlib failed at "C:\\cygwin64\\home\\appveyor\\.opam\\ocaml-variants.4.07.1+mingw32c\\bin\\dune.exe runtest -p ppxlib -j 2".
#=== ERROR while compiling ppxlib.0.9.0 =======================================#
# context              2.0.5 | win32/x86_64 | ocaml-variants.4.07.1+mingw32c | pinned(file://C:/projects/ppxlib)
# path                 ~/.opam/ocaml-variants.4.07.1+mingw32c/.opam-switch/build/ppxlib.0.9.0
# command              C:\cygwin64\home\appveyor\.opam\ocaml-variants.4.07.1+mingw32c\bin\dune.exe runtest -p ppxlib -j 2
# exit-code            1
# env-file             ~/.opam/log/ppxlib-992-9b8556.env
# output-file          ~/.opam/log/ppxlib-992-9b8556.out
### output ###
#       cinaps alias src/runtest (exit 2)
# (cd _build/default/src && .cinaps/cinaps.exe -diff-cmd -)
# Fatal error: exception Sys_error("C:\cygwin64\tmp\cinapsf14ff5.mli: Permission denied")
<><> Error report <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
+- The following actions failed
| - build ppxlib 0.9.0
+- 
- No changes have been performed

tests are broken at the tip of master (or, how do I run them right?)

At the tip of master, [dune runtest] fails. I've appended the output below.

I see that the build is good on TravisCI. Am I doing something wrong?

Here's my shell-file:

cd ~/sda/repos/ppxlib
eval $(opam env)
env
git reset --hard
git status
opam switch
dune installed-libraries
dune printenv
dune build
dune runtest

And here's the output:

Fri 16 Aug 2019 07:00:30 PM EDT
------------------------------------------------------------------------------------------
+ cd /home/dwang/sda/repos/ppxlib
++ opam env
+ eval 'OPAM_SWITCH_PREFIX='\''/usr/local/home/dwang/.opam/default'\'';' export 'OPAM_SWITCH_PREFIX;' 'CAML_LD_LIBRARY_PATH='\''/usr/local/home/dwang/.opam/default/lib/stublibs:/usr/local/home/dwang/.opam/default/lib/ocaml/stublibs:/usr/local/home/dwang/.opam/default/lib/ocaml'\'';' export 'CAML_LD_LIBRARY_PATH;' 'OCAML_TOPLEVEL_PATH='\''/usr/local/home/dwang/.opam/default/lib/toplevel'\'';' export 'OCAML_TOPLEVEL_PATH;' 'MANPATH='\'':/usr/local/home/dwang/.opam/default/man'\'';' export 'MANPATH;' 'PATH='\''/usr/local/home/dwang/.opam/default/bin:/tmp/Jane.shell-file.dwang:/j/office/app/emacs/dev/.sink-2019-08-08_20-17-22.814972/jane-elisp/bin:/home/dwang/bin:/home/dwang/.dispatch/bin:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/j/office/app/emacs/dev/jane-elisp/bin:/j/office/app/ocp-indent/prod/bin'\'';' export 'PATH;'
++ OPAM_SWITCH_PREFIX=/usr/local/home/dwang/.opam/default
++ export OPAM_SWITCH_PREFIX
++ CAML_LD_LIBRARY_PATH=/usr/local/home/dwang/.opam/default/lib/stublibs:/usr/local/home/dwang/.opam/default/lib/ocaml/stublibs:/usr/local/home/dwang/.opam/default/lib/ocaml
++ export CAML_LD_LIBRARY_PATH
++ OCAML_TOPLEVEL_PATH=/usr/local/home/dwang/.opam/default/lib/toplevel
++ export OCAML_TOPLEVEL_PATH
++ MANPATH=:/usr/local/home/dwang/.opam/default/man
++ export MANPATH
++ PATH=/usr/local/home/dwang/.opam/default/bin:/tmp/Jane.shell-file.dwang:/j/office/app/emacs/dev/.sink-2019-08-08_20-17-22.814972/jane-elisp/bin:/home/dwang/bin:/home/dwang/.dispatch/bin:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/j/office/app/emacs/dev/jane-elisp/bin:/j/office/app/ocp-indent/prod/bin
++ export PATH
+ env
CAML_LD_LIBRARY_PATH=/usr/local/home/dwang/.opam/default/lib/stublibs:/usr/local/home/dwang/.opam/default/lib/ocaml/stublibs:/usr/local/home/dwang/.opam/default/lib/ocaml
MANPATH=:/usr/local/home/dwang/.opam/default/man
HOSTNAME=igm-qws-u12112c
STABLE_BUILD_INFO=true
SHELL=/bin/bash
TERM=dumb
HISTSIZE=1000
RIPGREP_CONFIG_PATH=/home/dwang/.ripgreprc
KRB5CCNAME_STATIC=FILE:/tmp/krb5cc_dwang_static
DE=gnome
OLDPWD=/home/dwang/bin
OPAM_SWITCH_PREFIX=/usr/local/home/dwang/.opam/default
OPAMROOT=/usr/local/home/dwang/.opam
OCAML_TOPLEVEL_PATH=/usr/local/home/dwang/.opam/default/lib/toplevel
QT_GRAPHICSSYSTEM_CHECKED=1
USER=dwang
LS_COLORS=
BUILD_PROFILE=fast-build
PAGER=cat
INLINE_TEST_IN_PLACE=true
MAIL=/var/spool/mail/dwang
PATH=/usr/local/home/dwang/.opam/default/bin:/tmp/Jane.shell-file.dwang:/j/office/app/emacs/dev/.sink-2019-08-08_20-17-22.814972/jane-elisp/bin:/home/dwang/bin:/home/dwang/.dispatch/bin:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/j/office/app/emacs/dev/jane-elisp/bin:/j/office/app/ocp-indent/prod/bin
SANDBOX_RULES=true
HGUSER=Wang <[email protected]>
LC_COLLATE=C
PWD=/home/dwang/sda/repos/ppxlib
IRON_CONFIG=prod
EDITOR=emacsclient
IRON_USE_CHG=true
LANG=en_US.utf8
X_LIBRARY_INLINING=true
MODULEPATH=/usr/share/Modules/modulefiles:/etc/modulefiles
FEATURE_EXPLORER_FE_EXE=/tmp/dwang-emacs-exe-cache/j/office/app/fe/dev/bin/fe
FEATURE_EXPLORER_LOG_FE=false
LOADEDMODULES=
STACK_ROOT=/usr/local/home/dwang/stack
HISTCONTROL=ignoredups
KRB5CCNAME=FILE:/tmp/krb5cc_dwang_static
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SHARED_CACHE=true
CATALOG_BROWSER_BETA=1
HOME=/home/dwang
SHLVL=3
clones=/usr/local/home/dwang/
FE_CRS_SHOULD_UPDATE=true
LESS=-RXM
TMP=/tmp/dwang
LOGNAME=dwang
VISUAL=emacsclient
XDG_DATA_DIRS=/home/dwang/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share
PLUGD_INSTANCE=prod
MODULESHOME=/usr/share/Modules
LESSOPEN=||/usr/bin/lesspipe.sh %s
dst=nyc-qws-041
INFOPATH=/home/dwang/usr/local/share/info:
DISPLAY=localhost:15.0
OCAMLRUNPARAM=b=1
BASH_FUNC_undo-rebase()=() {  local f=$1;
 local new_base=$(fe show -base $f);
 local new_tip=$(fe show -tip $f);
 local merge=$(hg log -r "limit(reverse(only($new_tip,$new_base) and merge()),1)" -T "{node}\n");
 local old_tip=$(hg log -r "$merge" -T "{p1node}\n{p2node}\n" | grep -v "$new_base");
 local old_base0=$(hg log -r "$merge" -T "{desc}" | sed -rn 's/^.*rebase to .* with ancestor ([a-f0-9]*)$/\1/p');
 local old_base=$(echo "$old_base0" | sed -rn 's/^.*\[([0-9a-f]+)\]$/\1/p');
 if [[ -z "$old_base" ]]; then
 old_base="$old_base0";
 fi;
 hg book -f -r "$old_tip" "$f";
 hg push -B "$f";
 fe change -set-base "$old_base" "$f";
 fe tools wait "$f"
}
BASH_FUNC_module()=() {  eval `/usr/bin/modulecmd bash $*`
}
_=/usr/bin/env
+ git reset --hard
HEAD is now at 95ca88a Merge pull request #85 from ocaml-ppx/opam-file-fixes
+ git status
On branch print-diff-default-config
nothing to commit, working tree clean
+ opam switch
#   switch   compiler                    description
->  default  ocaml-base-compiler.4.07.1  default
+ dune installed-libraries
base                                (version: v0.12.0)
base.caml                           (version: v0.12.0)
base.md5                            (version: v0.12.0)
base.shadow_stdlib                  (version: v0.12.0)
bigarray                            (version: [distributed with Ocaml])
bytes                               (version: [distributed with Ocaml])
compiler-libs                       (version: [distributed with Ocaml])
compiler-libs.bytecomp              (version: [distributed with Ocaml])
compiler-libs.common                (version: [distributed with Ocaml])
compiler-libs.optcomp               (version: [distributed with Ocaml])
compiler-libs.toplevel              (version: [distributed with Ocaml])
dune._caml                          (version: 1.11.1)
dune._dune_lang                     (version: 1.11.1)
dune._ocaml_config                  (version: 1.11.1)
dune._result                        (version: 1.11.1)
dune._stdune                        (version: 1.11.1)
dune._wp                            (version: 1.11.1)
dune._wp.dune                       (version: 1.11.1)
dune._wp.jbuilder                   (version: 1.11.1)
dune.configurator                   (version: 1.11.1)
dynlink                             (version: [distributed with Ocaml])
ocaml-compiler-libs.bytecomp        (version: n/a)
ocaml-compiler-libs.common          (version: n/a)
ocaml-compiler-libs.shadow          (version: n/a)
ocaml-compiler-libs.toplevel        (version: n/a)
ocaml-migrate-parsetree             (version: 1.4.0)
ocaml-migrate-parsetree.driver-main (version: 1.4.0)
ppx_derivers                        (version: n/a)
ppxlib                              (version: 0.5.0)
ppxlib.ast                          (version: 0.5.0)
ppxlib.metaquot                     (version: 0.5.0)
ppxlib.metaquot_lifters             (version: 0.5.0)
ppxlib.print_diff                   (version: 0.5.0)
ppxlib.runner                       (version: 0.5.0)
ppxlib.runner_as_ppx                (version: 0.5.0)
ppxlib.traverse                     (version: 0.5.0)
ppxlib.traverse_builtins            (version: 0.5.0)
re                                  (version: 1.9.0)
re.emacs                            (version: 1.9.0)
re.glob                             (version: 1.9.0)
re.pcre                             (version: 1.9.0)
re.perl                             (version: 1.9.0)
re.posix                            (version: 1.9.0)
re.str                              (version: 1.9.0)
result                              (version: n/a)
seq                                 (version: [distributed with OCaml 4.07 or above])
sexplib0                            (version: v0.12.0)
stdio                               (version: v0.12.0)
str                                 (version: [distributed with Ocaml])
threads                             (version: [distributed with Ocaml])
threads.posix                       (version: [distributed with Ocaml])
threads.vm                          (version: [distributed with Ocaml])
uchar                               (version: [distributed with Ocaml])
unix                                (version: [distributed with Ocaml])
+ dune printenv

 ((flags
   (-w @[email protected]@30..39@[email protected]@[email protected] -strict-sequence
     -strict-formats -short-paths -keep-locs -w -66))
  (ocamlc_flags (-g))
  (ocamlopt_flags (-g))
  (c_flags
   (-std=gnu99 -O2 -fno-strict-aliasing -fwrapv -fno-builtin-memcmp -fPIC))
  (cxx_flags
   (-O2 -fno-strict-aliasing -fwrapv -fno-builtin-memcmp -fPIC)))

+ dune build
+ dune runtest
     patdiff (internal) (exit 1)
(cd _build/default && /usr/bin/patdiff -keep-whitespace -location-style omake -ascii test/code_path/test.ml test/code_path/test.ml.corrected)
-test/code_path/test.ml
+test/code_path/test.ml.corrected
File "test/code_path/test.ml", line 19, characters 0-1:

 open Ppxlib

 let () =
   Driver.register_transformation "test"
     ~extensions:[
       Extension.V3.declare "code_path"
         Expression
         Ast_pattern.(pstr nil)
         (fun ~ctxt ->
            let loc = Expansion_context.Extension.extension_point_loc ctxt in
            let code_path = Expansion_context.Extension.code_path ctxt in
            Ast_builder.Default.estring ~loc
              (Code_path.fully_qualified_path code_path))
     ]
 [%%expect{|
+Unknown directive `require'.
+Line _, characters 16-385:
+Error: This expression has type 'a list
+       but an expression was expected of type
+         Extension.t Ppxlib__Import.sexp_list
 |}]

 let s =
   let module A = struct
     module A' = struct
       let a =
         let module B = struct
           module B' = struct
             let b =
               let module C = struct
                 module C' = struct
                   let c = [%code_path]
                 end
               end
               in C.C'.c
           end
         end
         in B.B'.b
     end
   end
   in A.A'.a
 ;;
 [%%expect{|
-val s : string = "Test.s"
+Line _, characters 28-37:
+Error: Uninterpreted extension 'code_path'.
 |}]

 let module M = struct
   let m = [%code_path]
   end
   in
   M.m
 [%%expect{|
-- : string = "Test"
+Line _, characters 12-21:
+Error: Uninterpreted extension 'code_path'.
 |}]
     patdiff (internal) (exit 1)
(cd _build/default && /usr/bin/patdiff -keep-whitespace -location-style omake -ascii test/driver/non-compressible-suffix/test.ml test/driver/non-compressible-suffix/test.ml.corrected)
-test/driver/non-compressible-suffix/test.ml
+test/driver/non-compressible-suffix/test.ml.corrected
File "test/driver/non-compressible-suffix/test.ml", line 22, characters 0-1:
 open Ast_builder.Default;;

 Driver.register_transformation "blah"
   ~rules:[ Context_free.Rule.extension
              (Extension.declare "foo"
                 Expression
                 Ast_pattern.(pstr nil)
                 (fun ~loc ~path:_ -> eint ~loc 42))
          ; Context_free.Rule.extension
              (Extension.declare "@foo.bar"
                 Expression
                 Ast_pattern.(pstr nil)
                 (fun ~loc ~path:_ -> eint ~loc 42))
          ]
 ;;
 [%%expect{|
-- : unit = ()
+Unknown directive `require'.
+Unknown directive `require'.
+Line _, characters 9-405:
+Error: This expression has type 'a list
+       but an expression was expected of type
+         Context_free.Rule.t Ppxlib__Import.sexp_list
 |}]

 [%foo];;
 [%%expect{|
-- : int = 42
+Line _, characters 2-5:
+Error: Uninterpreted extension 'foo'.
 |}]

 [%foo.bar];;
 [%%expect{|
-- : int = 42
+Line _, characters 2-9:
+Error: Uninterpreted extension 'foo.bar'.
 |}]

 [%bar];;
 [%%expect{|
 Line _, characters 2-5:
 Error: Uninterpreted extension 'bar'.
 |}]
     patdiff (internal) (exit 1)
(cd _build/default && /usr/bin/patdiff -keep-whitespace -location-style omake -ascii test/deriving/test.ml test/deriving/test.ml.corrected)
-test/deriving/test.ml
+test/deriving/test.ml.corrected
File "test/deriving/test.ml", line 14, characters 0-1:
 #use "topfind";;
 #require "base";;
 #load "ppxlib_metaquot_lifters.cmo";;
 #load "ppxlib_metaquot.cmo";;

 open Ppxlib


 let foo =
   Deriving.add "foo"
     ~str_type_decl:(Deriving.Generator.make_noarg
                       (fun ~loc ~path:_ _ -> [%str let x = 42]))
 [%%expect{|
-val foo : Deriving.t = <abstr>
+Unknown directive `require'.
+Line _, characters 15-20:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let bar =
   Deriving.add "bar"
     ~str_type_decl:(Deriving.Generator.make_noarg
                       ~deps:[foo]
                       (fun ~loc ~path:_ _ -> [%str let () = Printf.printf "x = %d\n" x]))
 [%%expect{|
-val bar : Deriving.t = <abstr>
+Line _, characters 28-33:
+Error: This expression has type 'a list
+       but an expression was expected of type
+         Deriving.t Ppxlib__Import.sexp_list
 |}]

 type t = int [@@deriving bar]
 [%%expect{|
 Line _, characters 25-28:
-Error: Deriver foo is needed for bar, you need to add it before in the list
+Error: Ppxlib.Deriving: 'bar' is not a supported type deriving generator
 |}]

 type t = int [@@deriving bar, foo]
 [%%expect{|
 Line _, characters 25-33:
-Error: Deriver foo is needed for bar, you need to add it before in the list
+Error: Ppxlib.Deriving: 'bar' is not a supported type deriving generator
 |}]

 type nonrec int = int [@@deriving foo, bar]
 [%%expect{|
-type nonrec int = int
-val x : int = 42
+Line _, characters 34-42:
+Error: Ppxlib.Deriving: 'foo' is not a supported type deriving generator
 |}]
     patdiff (internal) (exit 1)
(cd _build/default && /usr/bin/patdiff -keep-whitespace -location-style omake -ascii test/base/test.ml test/base/test.ml.corrected)
-test/base/test.ml
+test/base/test.ml.corrected
File "test/base/test.ml", line 11, characters 0-1:
 #use "topfind";;
 #require "base";;
 #require "stdio";;

 open Base
 open Stdio
 open Ppxlib

 module N = Ppxlib_private.Name
 [%%expect{|
+Unknown directive `require'.
+Unknown directive `require'.
+Line _, characters 5-9:
+Error: Unbound module Base
+Line _, characters 5-10:
+Error: Unbound module Stdio
+Hint: Did you mean Stdlib?
 module N = Ppxlib.Ppxlib_private.Name
 |}]


 let dot_suffixes name =
   Caml.Printf.sprintf "%s"
     (Sexp.to_string_hum
        (List.sexp_of_t String.sexp_of_t (N.dot_suffixes name)))
 [%%expect{|
-val dot_suffixes : string -> string = <fun>
+Line _, characters 2-21:
+Error: Unbound module Caml
 |}]

 let _ = dot_suffixes "foo.bar.baz"
 [%%expect{|
-- : string = "(baz bar.baz foo.bar.baz)"
+Line _, characters 8-20:
+Error: Unbound value dot_suffixes
 |}]

 let _ = dot_suffixes "[email protected]"
 [%%expect{|
-- : string = "(bar.baz foo.bar.baz)"
+Line _, characters 8-20:
+Error: Unbound value dot_suffixes
 |}]


 let split_path name =
     let a, b = N.split_path name in
     Caml.Printf.sprintf "%s"
       (Sexp.to_string_hum
          (List [sexp_of_string a; Option.sexp_of_t sexp_of_string b]))
 [%%expect{|
-val split_path : string -> string = <fun>
+Line _, characters 4-23:
+Error: Unbound module Caml
 |}]

 let _ = split_path "a.b.c"
 [%%expect{|
-- : string = "(a.b.c ())"
+Line _, characters 8-18:
+Error: Unbound value split_path
 |}]

 let _ = split_path "a.b.c.D"
 [%%expect{|
-- : string = "(a.b.c (D))"
+Line _, characters 8-18:
+Error: Unbound value split_path
 |}]

 let _ = split_path ".D"
 [%%expect{|
-- : string = "(\"\" (D))"
+Line _, characters 8-18:
+Error: Unbound value split_path
 |}]

 let convert_longident string =
   let lident = Longident.parse string in
   let name = Longident.name lident in
   (name, lident)
 [%%expect{|
-val convert_longident : string -> string * longident = <fun>
+val convert_longident :
+  Ppxlib__Import.string -> Ppxlib__Import.string * longident = <fun>
 |}]

 let _ = convert_longident "x"
 [%%expect{|
-- : string * longident = ("x", Ppxlib.Longident.Lident "x")
+Line _, characters 26-29:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let _ = convert_longident "(+)"
 [%%expect{|
-- : string * longident = ("( + )", Ppxlib.Longident.Lident "+")
+Line _, characters 26-31:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let _ = convert_longident "( + )"
 [%%expect{|
-- : string * longident = ("( + )", Ppxlib.Longident.Lident "+")
+Line _, characters 26-33:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let _ = convert_longident "Base.x"
 [%%expect{|
-- : string * longident =
-("Base.x", Ppxlib.Longident.Ldot (Ppxlib.Longident.Lident "Base", "x"))
+Line _, characters 26-34:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let _ = convert_longident "Base.(+)"
 [%%expect{|
-- : string * longident =
-("Base.( + )", Ppxlib.Longident.Ldot (Ppxlib.Longident.Lident "Base", "+"))
+Line _, characters 26-36:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let _ = convert_longident "Base.( + )"
 [%%expect{|
-- : string * longident =
-("Base.( + )", Ppxlib.Longident.Ldot (Ppxlib.Longident.Lident "Base", "+"))
+Line _, characters 26-38:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let _ = convert_longident "Base.( land )"
 [%%expect{|
-- : string * longident =
-("Base.( land )",
- Ppxlib.Longident.Ldot (Ppxlib.Longident.Lident "Base", "land"))
+Line _, characters 26-41:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let _ = Ppxlib.Code_path.(file_path @@ top_level ~file_path:"dir/main.ml")
 [%%expect{|
-- : string = "dir/main.ml"
+Line _, characters 60-73:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let _ = Ppxlib.Code_path.(fully_qualified_path @@ top_level ~file_path:"dir/main.ml")
 [%%expect{|
-- : string = "Main"
+Line _, characters 71-84:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let complex_path =
   let open Ppxlib.Code_path in
   let loc = Ppxlib.Location.none in
   top_level ~file_path:"dir/main.ml"
   |> enter_module ~loc "Sub"
   |> enter_module ~loc "Sub_sub"
   |> enter_value ~loc "some_val"
 [%%expect{|
-val complex_path : Code_path.t = <abstr>
+Line _, characters 23-36:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 let _ = Ppxlib.Code_path.fully_qualified_path complex_path
 [%%expect{|
-- : string = "Main.Sub.Sub_sub.some_val"
+Line _, characters 46-58:
+Error: Unbound value complex_path
 |}]

 let _ = Ppxlib.Code_path.to_string_path complex_path
 [%%expect{|
-- : string = "dir/main.ml.Sub.Sub_sub"
+Line _, characters 40-52:
+Error: Unbound value complex_path
 |}]
     patdiff (internal) (exit 1)
(cd _build/default && /usr/bin/patdiff -keep-whitespace -location-style omake -ascii test/driver/transformations/test.ml test/driver/transformations/test.ml.corrected)
-test/driver/transformations/test.ml
+test/driver/transformations/test.ml.corrected
File "test/driver/transformations/test.ml", line 32, characters 0-1:
     match td.ptype_kind with
     | Ptype_record lds ->
       if Poly.(<>)
            (List.sort lds ~compare:(fun a b -> String.compare a.pld_name.txt b.pld_name.txt))
            lds
       then
         Driver.Lint_error.of_string td.ptype_loc
           "Fields are not sorted!"
         :: acc
       else
         acc
     | _ -> acc
 end
 let () =
   Driver.register_transformation "lint" ~lint_impl:(fun st -> lint#structure st [])
 [%%expect{|
-val lint : Driver.Lint_error.t list Ast_traverse.fold = <obj>
+Unknown directive `require'.
+Unknown directive `require'.
+Line _, characters 5-9:
+Error: Unbound module Base
+Line _, characters 9-18:
+Error: Unbound module Poly
+Line _, characters 62-66:
+Error: Unbound value lint
 |}]

 type t =
   { b : int
   ; a : int
   }
 [%%expect{|
-Line _, characters 0-36:
-Error (warning 22): Fields are not sorted!
+type t = { b : int; a : int; }
 |}]


 (* Extension with a path argument *)

 let () =
   Driver.register_transformation "plop"
     ~rules:[Context_free.Rule.extension
               (Extension.declare_with_path_arg "plop"
                  Expression
                  Ast_pattern.(pstr nil)
                  (fun ~loc ~path:_ ~arg ->
                     let open Ast_builder.Default in
                     match arg with
                     | None -> estring ~loc "-"
                     | Some { loc; txt } -> estring ~loc (Longident.name txt)))]
 [%%expect{|
+Line _, characters 11-418:
+Error: This expression has type 'a list
+       but an expression was expected of type
+         Context_free.Rule.t Ppxlib__Import.sexp_list
 |}]

 let _ = Caml.Printf.sprintf "%s\n" [%plop]
 [%%expect{|
-- : string = "-\n"
+Line _, characters 8-27:
+Error: Unbound module Caml
 |}]

 let _ = Caml.Printf.sprintf "%s\n" [%plop.Truc]
 [%%expect{|
-- : string = "Truc\n"
+Line _, characters 8-27:
+Error: Unbound module Caml
 |}]

 let _ = Caml.Printf.sprintf "%s\n" [%plop.Truc.Bidule]
 [%%expect{|
-- : string = "Truc.Bidule\n"
+Line _, characters 8-27:
+Error: Unbound module Caml
 |}]
     patdiff (internal) (exit 1)
(cd _build/default && /usr/bin/patdiff -keep-whitespace -location-style omake -ascii test/driver/attributes/test.ml test/driver/attributes/test.ml.corrected)
-test/driver/attributes/test.ml
+test/driver/attributes/test.ml.corrected
File "test/driver/attributes/test.ml", line 11, characters 0-1:
 #use "topfind";;
 #require "base";;

 open Base
 open Ppxlib

 let () = Driver.enable_checks ()

 let x = 1 [@@foo]
 [%%expect{|
-Line _, characters 13-16:
-Error: Attribute `foo' was not used
+Unknown directive `require'.
+Line _, characters 5-9:
+Error: Unbound module Base
+Line _, characters 30-32:
+Error: This expression has type unit but an expression was expected of type
+         Ppxlib__Import.unit
+val x : int = 1
 |}]

 let f x = 1 [@@deprecatd "..."]
 [%%expect{|
-Line _, characters 15-24:
-Error: Attribute `deprecatd' was not used.
-Hint: Did you mean deprecated?
+Line _, characters 6-7:
+Error (warning 27): unused variable x.
 |}]

 let attr : _ Attribute.t =
   Attribute.declare "blah"
     Attribute.Context.type_declaration
     Ast_pattern.(__)
     ignore
 [%%expect{|
-val attr : (type_declaration, unit) Attribute.t = <abstr>
+Line _, characters 20-26:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 type t = int [@blah]
 [%%expect{|
-Line _, characters 15-19:
-Error: Attribute `blah' was not used.
-Hint: `blah' is available for type declarations but is used here in the
-context of a core type.
-Did you put it at the wrong level?
+type t = int
 |}]

 let attr : _ Attribute.t =
   Attribute.declare "blah"
     Attribute.Context.expression
     Ast_pattern.(__)
     ignore
 [%%expect{|
-val attr : (expression, unit) Attribute.t = <abstr>
+Line _, characters 20-26:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
 |}]

 type t = int [@blah]
 [%%expect{|
-Line _, characters 15-19:
-Error: Attribute `blah' was not used.
-Hint: `blah' is available for expressions and type declarations but is used
-here in the context of a core type.
-Did you put it at the wrong level?
+type t = int
 |}]

 (* Attribute drops *)

 let faulty_transformation = object
   inherit Ast_traverse.map as super

   method! expression e =
     match e.pexp_desc with
     | Pexp_constant c ->
       Ast_builder.Default.pexp_constant ~loc:e.pexp_loc c
     | _ -> super#expression e
 end
 [%%expect{|
 val faulty_transformation : Ast_traverse.map = <obj>
 |}]

 let () =
   Driver.register_transformation "faulty" ~impl:faulty_transformation#structure

 let x = (42 [@foo])
 [%%expect{|
-Line _, characters 14-17:
-Error: Attribute `foo' was silently dropped
+Line _, characters 33-41:
+Error: This expression has type string but an expression was expected of type
+         Ppxlib__Import.string
+val x : t = 42
 |}]

 type t1 = < >
 type t2 = < t1 >
 type t3 = < (t1[@foo]) >
 [%%expect{|
 type t1 = <  >
 type t2 = <  >
-Line _, characters 17-20:
-Error: Attribute `foo' was not used
+type t3 = <  >
 |}]
------------------------------------------------------------------------------------------
Fri 16 Aug 2019 07:00:31 PM EDT

exited abnormally with code 1

Ignore more machine generated attributes

We currently whilelist merlin attributes as they can safely be dropped. Reason needs something similar, cf janestreet/ppx_inline_test#15.

To be more generic, we can simply whitelist all attributes with a name starting with _. We should also whitelist specific existing reason attributes to make things simpler.

Extend `-print-transformations`

I'm currently developing a deriving rewriter which is extending type-conv and is being registered using Type_conv.add "name". When I run -print-transformations the standalone rewriter gives me only type_conv. It would be great if it will print all the generators registered for type_conv.

P.S. I actually have a bug at the moment that standalone rewriter is not linked with my code and complains like Error: ppxlib_type_conv: 'name' is not a supported type type-conv generator. It would be great to know for sure (using switch above) that the library that registers generator is not linked.

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.