Commit Graph

309 Commits

Author SHA1 Message Date
Steve Cohen
3b605d1873 Typedefs in other modules weren't working properly (#275)
* Typedefs in other modules weren't working properly

Due to the way typedefs work (they're different than everything else),
resolution wasn't working properly on them, this commit adds proper
resolution so you can define them in remote modules.

Fixes #268
2017-09-20 08:20:32 -07:00
Jon Parise
75abb53499 Adopt the Ecto Credo configuration (#280)
This is a more liberal set of linter rules. I've made a few small
changes, but this is largely the same configuration that Ecto uses.
2017-09-19 14:38:38 -07:00
Jon Parise
3d237d95b0 Merge pull request #279 from jparise/merger
Merge 'thrift_tng' into master
2017-09-15 08:51:58 -07:00
Jon Parise
71b497d0e4 Merge branch 'thrift_tng' into master 2017-09-14 15:59:35 -07:00
James Fish
1434ab9adb Check erlang thrift files generated (#278) 2017-09-12 13:03:39 -07:00
James Fish
c2e4fef7f3 Fix annotation test module name (#277) 2017-09-12 09:39:46 -07:00
Jon Parise
5519008f00 Add support for Thrift annotations (#276)
Annotations appear within parentheses as part of various Thrift IDL
types such as fields and structures. They can be used to hint the
compiler or inform runtime behavior; the specification is pretty
open-ended.

This change adds support for parsing annotations (whose presence
previously resulted in parse errors) and storing them on our parser
models.

We currently support annotations on:

 - Enums
 - Fields
 - Functions
 - Services
 - Structures, Unions, and Exceptions

We can parse annotations on typedefs but don't currently store them
anywhere due to the way we internally represent a schema's typedefs as a
simple map of strings to types. We can revisit that later, perhaps as
part of a broader typedef refactoring.
2017-09-12 07:20:35 -07:00
Jon Parise
9dd4f0b4a1 Update dependencies to latest versions (#273)
This cleans up a handful of compilation warnings under Elixir 1.5.
2017-08-13 12:02:20 -07:00
Jon Parise
711e5e5a94 Address Elixir 1.5 compilation warnings (#271)
- to_char_list/1 -> to_charlist/1
 - use for comprehensions in place of Enum.filter_map/3
2017-07-30 17:20:00 -07:00
Jon Parise
e7ecc2b333 Include Elixir 1.5/OTP 20 in the build matrix (#272) 2017-07-30 16:04:51 -07:00
Jon Parise
fa175e5939 Upgrade to credo 0.7.3 (#269)
This also updates the .credo.exs configuration file and disables the
`ParenthesesOnZeroArityDefs` check because it crashes on our code base
(for some to-be-determined reason).
2017-04-25 15:53:13 -07:00
Jon Parise
0f09360090 Clean up service test shutdown (#263)
Also, remove some unused variables.
2017-04-25 15:25:14 -07:00
Jon Parise
909126ab03 Mark local to_thrift/2 functions as private (#267)
Also, the generated inner BinaryProtocol module doesn't need
documentation.
2017-04-14 11:01:52 -07:00
Jon Parise
8fc39a292d Prefer try do .. after .. end for cleanup (#265)
This syntax is shorter, gurantees cleanup, and preserves stacktraces
(unlike re-raising).
2017-04-08 08:26:19 -07:00
Jon Parise
566c9772b2 Enforce that all exception keys are set (#264) 2017-04-07 17:25:45 -07:00
Jon Parise
bbb29ceb30 Make better use of the with syntax (#260)
These two places where pairing a `with` with a `case`, but they can both
be expressed using just a `with` statement.
2017-04-07 15:52:34 -07:00
James Fish
0ebb91e0aa Fix line numbers for thrift tests (#262)
Create/register tests inline in test module so that test line numbers
can be used with `mix test`.
2017-04-06 19:17:25 -07:00
Jon Parise
1a9293bfeb Revise TApplicationException (#259)
This brings TApplicationException more in line with Elixir's conventions
for creating exceptions. `exception/1` now expects a keyword list with a
:type and an optional :message value. :type can either be an integer or
one of our internal atoms (e.g. :invalid_protocol).

Only the atom form is stored internally. It can be lazily converted back
to an integer at serialization time using `type_id/1`. We don't store
the integer form up-front because it's only used in "exceptional" (ha!)
cases in the server code path.

The list of well-known exception types has been expanded based on the
current Apache Thrift 0.10.0 release (C++ implementation).

Lastly, TApplicationException now has dedicated unit test coverage.
2017-04-06 07:51:32 -07:00
Jon Parise
4954dee60c Constant modules now reuse suitable existing names (#257)
We were previously writing constants to modules named after their
originating files. This approach was unfortunately too naive because the
resulting module names could conflict with other generated modules that
are spelled the same but different only in letter case.

For example, for a file named `myservice.thrift`:

    const float PI = 3.14
    service MyService {}

We would previously write the constants to `myservice.Myservice`
(`myservice.ex`) and the service definition to `myservice.MyService`
(`my_service.ex`). We now write both to `myservice.MyService` (thanks to
the generator's constants-merging logic) and no longer run into module
name conflicts.
2017-03-28 13:03:14 -07:00
Jon Parise
70d6e66db7 Use "Thrift.Generated" as the default namespace (#255)
Previously, we defaulted to generating our output files in the root, but
that behavior is potentially dangerous because it could class with other
top-level module names.

It's still possible to write files to the root by specifying `""` as the
namespace.
2017-03-16 09:06:44 -07:00
Jon Parise
fe1ed8611c Remove the implicit field identifier warning (#252)
This is useful information, but we don't currently have a good way to
capture it for later display. As is, printing out these warnings as part
of the parsing path can product a bit of spurious output as part of
normal operations because our mix compile task always parses .thrift
files to determine their build targets.
2017-03-14 19:58:39 -07:00
Steve Cohen
9d2dfa0ebe Fixed Global Resolution... again (#254)
Fixes #253

The problem with the last fix is that generation is recursive. This
means we should be cleaning up each nested context's local lookups when
we move to a deeper context. Otherwise, we're relying on map iteration
order to override a particular value. If the value comes after the one
wer're inserting, we're out of luck.

This PR starts from a known good state with no overrides, and on each
subsequent generation, builds a new resolution map.
2017-03-14 17:36:39 -07:00
Jon Parise
6872e42fec Allow the default namespace to be nil (#251)
Change #228 allowed the default namespace to be either an atom or a
string, but we still want to special-case the `nil` atom to mean "no
default namespace". Otherwise, we get a namespace named "Nil".
2017-03-14 10:26:52 -07:00
Steve Cohen
ab561e2558 Fixed Global resolution (#253)
We had the concept of global resolution, that is, we would place the
current module's definitions in a global scope. This would have worked,
but it wasn't deterministic.

Now, we tell the file group which module is local, and have it update
its resolutions with only the local definitions when we build their
data. This limits the scope of global variables.
2017-03-13 09:52:58 -07:00
Steve Cohen
76ee96cb29 Readme faq (#162)
* Added a small FAQ to the readme

* Updated link

* Prettified link

* Fixed language a bit

* PR changes
2017-03-07 10:03:14 -08:00
Jon Parise
11810e5999 Deterministically merge FileGroup resolutions (#250)
This is an incomplete solution to the fact that our existing resolution
merge was non-deterministic with regard to non-qualified names. We now
always take the newer value for a conflicting key, which gets us closer
to the expected behavior.

We unfortunately still aren't implementing the correct scoping rules.
For example, this still allows a non-qualified name to "leak" from one
included file into another. This should be revisited and addressed with
a more complete solution.
2017-03-07 09:01:35 -08:00
Jon Parise
d57f1bf14a Upgrade to dialyxir 0.5.0 (#247)
This is a significant dialyxir release because it now uses the :dialyzer
module directly instead of spawning a separate VM process in which to
run the dialyzer command line process. More details here:

https://github.com/jeremyjh/dialyxir/wiki/Upgrading-to-0.5

Fortunately for us, we get all of the benefits of this release without
having to make any specific changes to our project configuration.
2017-03-06 09:07:40 -08:00
Jon Parise
a73154157b Use negative values for implicit field indices (#249)
The Apache Thrift compiler auto-assigns negative values (starting at -1)
to fields with implicit indices. This changes our code to do the same.
Because field identifiers are serialized on the wire, it's important for
our implementations to match in this regard.

This also moves our implicit identifier warning directly into the
parser. This was the only warning being generated from the higher level
Thrift.Parser code; all other error conditions raise exceptions. This
also lets us remove the now-unused Thrift.Parser.Shell module.

The only downside to moving the warning into the parser is that we lose
a little context (the name of the parent struct), but because we now
include a line number and this is such a rare case, this feels like a
reasonable trade-off.
2017-03-03 09:38:11 -08:00
Jon Parise
6fc7e069ac Parsed models now have line numbers (#248)
This change extracts line numbers from the lexer's tokens and assigns
them to our model structures for later use in error messages or for
debugging.

This change also generalizes model construction by introducing
build_model/2 and build_model/3.
2017-03-01 13:49:21 -08:00
Jon Parise
ff86f1e2a2 Fix up a number of model-related dialyzer warnings (#246)
1. Always refer to models types using their `t` type so we get full
   dialyzer visibility into argument and return values.
2. Many field names are atoms now and shouldn't use String.t.
3. Fix the field list and enum list types, which are lists and not maps.
4. Suppress dialyzer warnings in generator/service.ex due to a mismatch
   between response field types and function return types.
2017-03-01 07:59:28 -08:00
Jon Parise
48e87e627a Improve the grammar's Thrift IDL compliance (#245)
This is a near complete rewrite of our yecc grammar. It includes the
following changes:

1. Our symbols now follow Erlang's TitleCase naming convention. This
   makes them easier to differentiate from literals and tokens.
2. We no longer support set constants because they aren't supported by
   the Apache Thrift compiler.
3. We no longer support parens around a service definition's "extends"
   clause. That was introduced by mistake and isn't part of the official
   Thrift IDL specification.
4. We now support both `,` and `;` as list separators. We previously
   only supported ',' while ';' was discarded by the tokenizer.
2017-02-28 14:03:01 -08:00
Steve Cohen
1168b29d40 Connections couldn't be closed (#244)
* Connections couldn't be closed

Previously, connections would reconnect if they were closed by calling
`Client.close`. This is bad behavior, and could easily lead to running
out of sockets. `Client.close` now closes the TCP connection and stops
the process.
2017-02-27 13:19:55 -08:00
Jon Parise
7fd72cdfb4 Simplify the lexer's rules (#243)
There are multiple of small changes here that result in a large
simplification of the lexer's rules:

1. Many rules that were mapping literals to atoms have been collapsed in
   favor of generic rules that call list_to_atom/1 on TokenChars.
2. Quoted string processing now uses a simpler regular expression paired
   with a processing function, giving us support for character escape
   sequences.
3. The st_ident token type has been removed because Smalltalk-specific
   IDL support was deprecated a long time ago by Apache Thrift and we
   don't generate Smalltalk namespaces in our library.

Also, the lexer unit test now verifies that we can tokenize a complete
document.
2017-02-27 11:41:07 -08:00
Jon Parise
bd4f524bd6 Implement Exception.message/1 for exceptions (#242)
We generate Elixir exception structs via defexception for Thrift
exception types. That macro always provides an exception/1
implementation, but message/1 is only generated if the exception is
defined with a :message field. When it doesn't, the generated code
produces a warning because the full Exception behaviour hasn't been
implemented.

This change generates a message/1 implementation for all of our Thrift
exception structs which don't have a natural :message field. It returns
the Kernel.inspect/2 string representation of the exception, which is
both simple and unambiguous.
2017-02-16 13:58:30 -08:00
Jon Parise
3b3ce1b501 Update a bunch of dependencies (#240)
In particular, this updates ranch to the 1.3.x series, which includes a
nice set of small bug fixes and improvements:

https://github.com/ninenines/ranch/blob/master/CHANGELOG.asciidoc#130
2017-02-16 08:52:36 -08:00
Jon Parise
b191c6b989 Revise our Dialyzer configuration (#241)
This upgrades dialyxir, simplifies the set of included apps, and adds an
"ignore" rule for the leex-related warning that we otherwise can't
control.
2017-02-16 08:51:29 -08:00
Jon Parise
87df071fc0 Include our package version in the manifest header (#238)
This gives us additional control over when to consider existing
artifacts as "stale" and in need of regeneration. At the moment, we
require a strict version string match, but as we mature in accordance
with semver guidelines, we can switch to a more fine-grained approach
based on Version.match?/3.

We don't need to bump the manifest version in this case because ...

1. Reads against existing manifests will safely fail and result in
   desirable rebuild behavior.
2. The manifest stuff is brand new, and it's unlike many folks are
   using it yet.
2017-02-14 11:09:36 -08:00
Jon Parise
359d70a1cb Maintain a manifest of generated files (#234)
This is signifiant rewrite of the compiler task to support manifests,
making us a better mix citizen and adding support for `mix clean`-based
file cleanup.

Our manifest is written as a binary-encoded Erlang term to give us the
most forward-compatible file format. At the moment, we only store a
manifest version number and the list of generated files, but we may
expand this in the future to include additional dependency information
such as the "version" of the code that was used to generate these files.

See #138 for a more detailed discussion.
2017-02-11 18:32:30 -08:00
Jon Parise
2b4479a79b Update remaining integers to use signed values (#237)
This continues the work started in #233 to address #206.
2017-02-11 18:31:57 -08:00
Steve Cohen
af41860c95 Serializer / Deserializer do not respect signedness (#233)
We were using size(x) rather than x-signed; apparently, size(x) is for
unsigned numbers.

Fixed, and added unit tests for boundary conditions. Fixes #206
2017-02-11 13:55:14 -07:00
Dan Swain
de61caa2a5 Merge pull request #230 from dantswain/incursion
Handle constants clobbering other modules or double-defining
2017-02-11 14:43:12 -05:00
Dan Swain
2ffb4afd7d Merge pull request #231 from dantswain/parser_opts_dialyzer
Misc dialyzer fixes
2017-02-10 20:09:27 -05:00
Dan Swain
ca04721384 Misc dialyzer fixes 2017-02-10 17:31:52 -05:00
Dan Swain
93fa959733 Ebert cleanup 2017-02-10 17:12:17 -05:00
Dan Swain
282374481d Do not generate a module with no code 2017-02-10 17:06:02 -05:00
Dan Swain
9bb49b3a2d Merge branch 'thrift_tng' into incursion 2017-02-10 16:51:40 -05:00
Dan Swain
fb881c5498 Only write constants defined in current group 2017-02-10 16:50:56 -05:00
Dan Swain
c8eb0cd78d Merge pull request #228 from dantswain/namespace_module
Allow the namespace option to be a string or module
2017-02-10 16:31:41 -05:00
Dan Swain
2823ad2dc3 Add a failing test
Still not able to reproduce an overwrite but this is definitely a bug.
2017-02-10 13:06:56 -05:00
Dan Swain
d73713fc13 Fix async tests 2017-02-10 12:40:31 -05:00