v12.0.0
Changelog
v12.0.0
Breaking Changes
- #2179 - @KronicDeth
- Drop support for Elixir <= 1.6.
Continuing support for Elixir <= 1.6 required special handling of the language level to support differences in precedence and operators. Removing the language level tracking allows dropping theLevelandFilePropertyPusherclasses and all their usages, including in the parser grammar and the specialifVersionexternal rule. It also eliminates the need for tests to setup the SDK since it was only needed to get the appropriate Level. This makes the tests run in 45 seconds instead of 7 minutes.
- Drop support for Elixir <= 1.6.
- #2339 - @KronicDeth
- Remove code dependent on the Erlang plugin
Erlang plugin is not 2021.3 compatible yet
- Remove code dependent on the Erlang plugin
Enhancements
- #2179 - @KronicDeth
- Resolve more calls and bindings in
Ecto.Querycalls-
Resolve bindings in
Ecto.Query.lock/3 -
Resolve bindings in
Ecto.Query.windows/3 -
Walk
preloadbinding and expression -
Resolve in
updatefor Ecto.Query -
Resolve
fragmentinwith_cte -
Resolve
bindingandexprinEcto.Query.dynamic/1-2 -
Resolve
fieldinjoin(..., on: field(..., ...) ...)
Forjoin/5, descend into the options to look foron: valueand then walkvaluethe same as the value tohavingorwherein selects since they're all boolean conditions. -
Extract
ecto.query.Nested -
Resolve
Ecto.Query.WindowAPIfunctions -
Resolve reference variable src in
join(query, ..., [{src, counter}], ..., ...)
Tuple lists in join have two forms:{^assoc, a}{src, counter}
The pinned association form was already handled because the second element was checked for a declaration, but the first element was not, so
srcin (2) could not be resolved. -
from([..] in ...) -
Treat
or_havingthe same ashaving -
Treat
or_wherethe same aswhere -
Treat
having:the same aswhere:infrom -
Treat
select_mergethe same asselectfor resolvingEcto.Query.API. -
from(..., [elements]) -
Resolve Ecto reference variables in
left in ...
-
ModuleWalkerandNameArityRangeWalker
Reduce code by abstracting common pattern for walking library APIs.ExUnit- Find modules declared in
tests. - Resolve call definitions inside
describeblocks. - Resolve variables in
assert_receiveandassert_received. - Resolve
aliasto modules defined inside the enclosingdescribeblock. - Walk
assertexpression for variable declarations- Check for earlier bindings of variables in right operand of
=inassert.
- Check for earlier bindings of variables in right operand of
- Find modules declared in
- Resolve
requireas:arguments as Aliases - Decompiler
- Erlang
- Decompile private Erlang functions
- Decompile specs from Erlang DbgI
- Decompile function bodies from Erlang DbgI
- Escape "in" when an Erlang Var in type
- Decompile types from Erlang DbgI
Fixes #2017
- Decompile Elixir function bodies using DbgI
- :erlang./(a, b) -> a / b
- :erlang.*(a, b) -> a * b
- Convert :erlang.==(a,b) to a == b
- Rewrite case to
andwhen there is a badbool error too - Decompile %{struct: name, ...} as %name{...}
- Rewrite more :erlang functions to Elixir
- Rewrite case to ||
- Rewrite
case expr1 do pat1 -> true; _ -> false; endtomatch?(pat1, expr1) - Rewrite
if var do false else trueto!var - Rewrite case to or
- Rewrite case to and
- Rewrite :erlang.error(E.exception(M)) to raise E, M
- Rewrite case statements to if
- Rewrite case statements to &&
- Indent all lines of spec macro string in case it is multiple @SPEC
- Erlang
- Resolve module attributes defined outside the immediate modular lexical scope
-
Resolve module attributes registered in
elixir_module.erlto decompiled source- after_compile
- before_compile
- behaviour
- compile
- derive
- dialyzer
- external_resource
- on_definition
-
Index module attributes
Use the index to resolve module attributes when it can't be found directly by tree walking.- Defined with
Module.put_attribute/3 - Defined with
Module.register_attribute/3 - Defined in
quoteblocks
- Defined with
-
- Resolve variables to variables in any
quoteblocks
If a variable can't be resolved in the scope, try resolving it to any variable declared at the top-level of aquoteblock. This helps with certain patterns ofquoteblocks used in Ecto where a variable's declaration and usage are not in the samequoteblock. - Simplify CallDefinitionClause resolver for modulars
Due to the addition of skipping if the entrance is a child call, the check for only following siblings is no longer needed. Additionally, that check caused a bug because theElixirStabBodyskipped call definitions because they were new scopes. - Resolve functions declared with
Mix.Generator.embed_templateandembed_text.
Also, new system for tracking resolves paths -imports,defdelegate, andusecalls are added to the resolve results after the preferred elements are chosen for source in the same module. This prevents only theimportshowing because the actual declaration is in another module or the SDK. - Record a call as visited in its
treeWalkUpinstead of requiring the caller to do it in the passResolveState- Record
quoteCallas visited inQuoteMacro.treeWalkUp - Record
unquoteCallas visited inUnquote.treeWalkUp - Record
unlessCallas visited inUnless.treeWalkUp - Record
ifCallas visited inIf.treeWalkUp - Record
importCallas visited inImport.treeWalkUp - Record
useCallas visited inUse.treeWalkUp
- Record
- More macro specialized in Structure View
testdescribe
- Resolve
Qualifer.unquote(variable)(...)to any definition with correct arity inQualifier. - Only calculate
element.resolvedFinalArityonce inresolvedQualified - Implementations and Protocols
- Redo icons
- Implementations Go To Protocol line markers
- Go to Super for calls to
defimplfunction/macro
Goes to corresponding name/arity in thedefprotocolthat thedefimplimplements. - Go to implementations line marker from defprotocol def
- Go to implementations line marker from defprotocol
- Go To Implementation from individual functions in defimpl
- Go To Implementation from defimpl Alias
- Resolve protocol function to
defin defprotocol - Resolve defp inside of defimpl
Process declarations inside of implementation the same as modules.
- Stop
prependQualifiersat top of file - Walk the false and true (else) branch of unless in Modules or Quote
- Walk the true and false (else) branch of if in Modules or Quote
- Port
preferredandexpandsystem fromCallablestoModules. - Update CI build dependencies
- Resolve more calls and bindings in
- #2199 - @KronicDeth
- Regression test for #2198.
- #2201 - @KronicDeth
- Use callbacks as completions for calls.
- #2223 - @KronicDeth
- Decompiler
- Don't require
MacroNameArityforaccept, but useNameArityonly because no decompiler cares about the macro.
- Don't require
- Tests for Code.Identifier and String.Tokenizer
- Decompiler
- #2226 - @KronicDeth
- Structure View for
EEx.function_from_(file|string) - Variants (completion) for functions declared by special macros.
- Functions defined by
EEx.function_from_(file|string) exception/1andmessage/1defined bydefexception*_text/0and*_template(assigns)functions defined byMix.Generator.embed_textandMix.Generator.embed_template.
- Functions defined by
- Structure View for
- #2334 - @KronicDeth
- Internal Tool for BEAM Bulk Decompilation
Decompile all .beam files in the project, modules and SDKs to check for errors in the generated Elixir code - Decompiler
- Erlang Abst
- Log decompilation errors
- Erlang Abst
- Error Reports
- Include system information in error reports
Instead of just including the plugin version, also include the Application name, edition, and version; and the Operation System name and version as these are common follow-up questions I have. - Remove tab at start of location for title of issues
- Don't include "java.lang.Throwable: " in title of issues
TheThrowableis necessary to get a stacktrace, but not a real error.
- Include system information in error reports
- Internal Tool for BEAM Bulk Decompilation
- #2339 - @KronicDeth
- Build against
2021.3 runPluginVerifierin GitHub Actions- Update IDEA version range supports and verified
- Fix reported compatibility warnings
- Inline deprecated bundle messages
- Don't bundle built-in markdown plugin, depend on it instead
- Build against
Bug Fixes
- #2074 - @Thau
- Alternative function clause for
put_eventwithsuite_finished
- Alternative function clause for
- #2179 - @KronicDeth
- StackOverflow fixes
-
getElementDescription(ElixirAtom, ElementDescriptionLocation)
Override getElementDescription for atoms to prevent StackOverflow while looking for a provider. -
Don't descend into either branch of
iforunlessif entrance in either branch when resolving calls.
If the definition were in one of the branch, it would already have been found on processing previous siblings in theElixirStabBody. -
Treat child of modulars as being at the same level if nested in
iforunless
Preventstestinifinsupervisor_test.exsinectofrom stack overflowing. -
Fix StackOverflow when looking for earlier bindings in parameters.
-
Don't check following siblings of modulars if entrance is a direct child
Prevent StackOverflow when trying to resolve embed_template when more than one appears in the same module.
In general, if the entrance is a child of modular then it can only be defined by a previous sibling, usually an
importoruse, but if the entrance is descendant of a child, then it child then it may be a call to a function or macro defined in the modular to following siblings of the entrance ancestor child needs to be checked if the entrance is a forward-call to a later declared function or macro. -
Fix StackOverflowError in ifErlangRewriteTo
Don't rewriter :erlang.* to a different :erlang.*
-
- Adjust nameArityInterval in nameArityInAnyModule
Ensures thatfragment/1..used in aquotecan resolve to one inEcto.Query.API. - Resolve variable that are the only child of
quote
Ecto loves doingquote do: queryor other variable names in the code and tests, so record those as declarations to resolve as invalid results. - Find enclosing macro call when keyword
do:is surrounded by parentheses
Previously, onlyquote do: variablewould work, but nowquote(do: variable)also works to find thequotecall. - Don't mark fields and keys that are not expected to be resolvable yet as unresolvable in Elixir References inspection.
QualifiedBracketOperationqualifierStructOperationqualifier- Expect qualified
unquoteto only have invalid results. - Don't mark invalid only results for first chained function call.
Don't reportunquote(schema).__schema__(:source) - Don't mark invalid only results for chained function calls.
Don't reportMix.shell().yes?("Are you sure you want to drop the database for repo #{inspect repo}?") - Don't mark invalid resolved function of call output
- Don't mark invalid resolved keys or fields of call output
- Don't mark invalid resolved function call on keys or fields
- Don't mark parentheses calls on variables if the call does not resolve.
Can't find exact valid resolves on variables yet. - Don't mark keys or fields on the output of a function call.
- Fix some bugs with
Ecto.Querycalls.- Add missing
state.put(Query.Call, call)forjoin/3-4executeOnIn. - Walk the operands of
|>inselectexpressions. - Resolve pinned variables as normal instead of as reference variables for Ecto.Query calls.
- Don't walk keywords that cannot declare reference variables.
hintslockintersectintersect_allexceptexcept_allunionunion_allprefixpreloadoffsetwindowslimit
- Don't treat signature for call definition as use of Ecto macro
- Add missing
- Don't generate references to aliases, functions, or types that don't have declarations
assoc/2injoin: .. in assoc(_, _)in a no parenthesesfromcallvarin type restrictions
Related to elixir-ecto/ecto#3756BitString
BitStringis recognized indefimpl ..., for: BitStringto define protocol implementations for<<..>>, but theBitStringmodule itself does not exist, so can't be resolved.
- Error reporting
- Ignore
at com.intellij.openapi.diagnostic.Loggerwhen calculating location for error report titles - Improve error report format sent to GitHub
- Fix the
eventmessagenot being included, which meant that the excerpt wasn't included, so no reproducibility or element class was available. - Filter stacktrace to stop at last line from the plugin to limit their size and improve chance of URL being short enough for GitHub.
- Don't include "What I was doing" section unless user actually fills in the additional information in the UI form.
I'm sick of seeing the issue tracker full of "I don't know what I was doing", which is the default text when no additional info is given in the UI form. - Set title to the message at start of exception and first
atthat isn't from theerrorreport.Loggerinstead of[auto-generated]as this is the pattern I follow when renaming manually.
- Ignore
- Handle
alias __MODULE__.{...}in prependQualifier
Fixes #2153 - Log error, but don't fail with TODO() for unknown strippedQualifier or null qualifier
Fixes #2153 - Go To Declaration for captures
-
Don't allow name to be acceptable named parent in
&name/arity.
Resolves #488
Allows Go To Declaration onnameand not just on/arity. -
Don't allow
Mod.nameto be acceptable named parent in&Mod.name/arity.
Resolves #488
Fixes #2101Allows Go To Declaration on
nameand not just on/arity. -
Resolve
&name/arityand&Mod.name/arityusing same code as callables.
Fixes resolving &Mod.name.arity and ensures that special handling for weird definitions for callables also apply to captures.
-
- Resolve
__MODULE__inquotetodefmacro __MODULE__inKernel.SpecialForms - Performance
-
Fix String.Unicode decompiled being PlainText instead of Elixir
String.Unicodewhen decompiled using all information fromDbgIwas 161,171 lines long, which made the JetBrains API treat it as plain text instead of Elixir. Being that long also made it freeze the UI while being decompiled.Now, don't even attempt to use the
DbgIif the function has more than 10 clauses. -
Don't decompile private macros and functions if > 500 definitions in one module.
-
If body cannot be decompiled, decompile as one-liner with
...body -
Don't decompile Abst clause bodies that exceed 1024 bytes.
-
Decompile Erlang one clauses as Elixir one-liners
-
- Fix resolving type specs
-
Find
ancestorTypeSpecfor qualified type used in parentheses in anonymous function type in an alternation@type run :: ((Ecto.Repo.t, changes) -> {:ok | :error, any}) | {module, atom, [any]} -
Resolve type parameters used in inline anonymous function types
-
Resolve callback heads to themselves when they have type restrictions using when
-
Ignore literal parameters
- Decimals
- Aliases
-
Check left operand of
\\for type parameters as they could appear when copying def with defaults.
-
- putInitialVisitedElement in variable.Variants
Fixes #2002 - Walk
defdelegates when walkingimports
Fixes resolvingconfigfromuse Mix.Configas it delegates toConfig - Resolve variables used in
match?guards to pattern declaration
Resolveson_deleteinmatch?(%{on_delete: on_delete} when on_delete != :nothing, reflection) - Implementations and Protocols
- Fix calculating definition for stubs of defimpl with
for:
There was no clause for defimpl being arity 3, which is the case when there is the (1) protocol (2)for:and (3)doblock. Not having a definition meant that thedefimpl protocol, for: struct dowould be in AllName index, but not ModularName. - Get name of enclosing modular for defimpl without
for:
- Fix calculating definition for stubs of defimpl with
- Store if stubs are guards
- Decompiler
- Surround
casestatements with parentheses when used incondclause conditions - Convert OtpErlangString to OtpErlangList for tuple and call argument lists
- Escape ESC character as \e
- Handle Clause arguments being OtpErlangString
- Handle tuple elements being an OtpErlangString
- Add missing
.after callee when it is a module or fn - Protect from
Macro.toString(macro)StackOverflowError when decompiling body of function clauses - Don't print function names as atoms in captures
- Escape
\xto\\xin OtpErlangStr - Fix rewrite of :erlang calls
- Surround type unions with parentheses
Prevents parsing problems with unions in guard (when) clauses - Don't use
prependIndentbecause it indents blank lines too.
This doesn't matchmix formator the IntelliJ Elixir formatting. - Erlang
- Escape fn Erlang variable
- Escape Erlang char \ as \
- Don't append lines for clauses or
afterin Erlangreceivewhen decompiling if empty. - Use
Infix,Prefix, andUnquotedecompolers for Erlang Abst chunk in addition to DbgI chunk- functions
- typesepcs
- Use
function.macroNameArityMacro.macrowhen decompiling Erlang Abst clauses.
Don't usedefanymore when unexported and therefore private; usedefpinstead. - Remove space after
...in decompiled private types.
- Surround
- Process imports for calls
Imports were previously only processed inside of Modules and not in general, which means that imports in the file were not processed, which is needed forassociation.exin Ecto. - Classify ..// as OTHER instead of NOT_CALLABLE, so that it is escaped as a key.
- Fix Macro.ifCaptureModuleNameArity
- Resolve variable to parameter in
%parameter{}patterns for struct names Unquote.treeWalkUpUnquotedthrough tuplesQuote.treeWalkUpthroughcase- Stop searching on numerical index in binding
- Stop searching if atom in wrong place in binding
Stops invalid binding test from erroring when resolving it. - Turn off
tailrecbecause it doesn't work correctly forElixirAccessExpression - Stop searching for qualifier when
ElixirUnqualifiedNoParenthesesManyArgumentsCall.
- StackOverflow fixes
- #2199 - @KronicDeth
- Stop highlighting types when
unquote_splicing/1is reached.
unquote_splicingis being used to splat arguments or fields of a struct into the type. The arguments tounquote_splicingare normal calls or variables, not types.
- Stop highlighting types when
- #2201 - @KronicDeth
- Implement
call_definition_clause.Variants#executeOnCallback
- Implement
- #2204 - @KronicDeth
CallDefinitionClause.time/1- Mark guards as runtime.
- Mark anything unknown as runtime too.
- Log unknown calls.
- #2207 - @KronicDeth
- Check if
CallisValidbefore usingcontainingFileforlocationString.
- Check if
- #2208 - @KronicDeth
- Check if
projectis not dumb innameArityInAnyModule.
- Check if
- #2209 - @KronicDeth
- Take
resolveInScopeonly if at least one valid
Checking only for an empty collection allowed any prefixes in the scope to override exact matches in anywhere indexed, which meant thatEctoindefmodule Ecto.Adapter doresolved to itself instead of the exactdefmodule Ecto do.
- Take
- #2214 - @KronicDeth
- When regenerating the parser,
ElixirVisitoris also regenerated. When it was regenerated it lost the bug fix for#visitLiteralSigileLinecalling itself. Added a regression test, so that this can't happen again.
- When regenerating the parser,
- #2223 - @KronicDeth
- Ecto
- Walk keyword keys as right operand of
ininfrom
- Walk keyword keys as right operand of
- Resolving type references
- Walk struct operations for type parameters
- Check keyword values for type parameters
- Check operands of two operations for type parameters
- Stop looking for type parameters on qualified or unqualified alias
- Decompiler
- Only unquote
inwhen an Erlang function, otherwise, use operators the same as Elixir for defs and calls. - Fix apply Erlang arguments, so that they are inside
[]. - Quote keyword keys containing
-
Fixes decompiling ofElixir.Phoenix.HTML.Tag.beam - Use apply with escaped atom when Erlang function call is an Elixir operator
- Only unquote
- Port String.Tokenizer.tokenize for use in Identifier.inspectAsKey
I was putting off porting all ofIdentifer.inspectAsKeyby adding special cases as needed, but the decompiler kept having bugs, so port all of it includingString.Tokenizer.tokenize. It will also work for unicode characters now too. - Resolve calls that are unquoted values to search for quote blocks in those functions.
- Stop looking for qualifiers to prepend when reaching
=> - The parent argument to AccumulatorContinue.childExpressionsFoldWhile should be this and not parent
When converting to an extension function I leftparentin place because the argument is calledparent, but since it is an extension function that value becausethis.parentwhen it really should have beenthis. Usingthis.parentmeant it would ask for the parent's children and keep looping back tothis. - Don't use
tailrecin function with any body-recursion.
It causes issues withElixirAccessExpressionrecursion sometimes.
- Ecto
- #2226 - @KronicDeth
- Implement completion for functions declared with
defdelegate. - Fix
LookupElementPresentation.putItemPresentationaddTailText.
Only append suffix ofpresentableTextif it is prefixed byitemText.
- Implement completion for functions declared with
- #2334 - @KronicDeth
- Decompiler
- Elixir
- Decompile local function calls in Elixir DbgI using inspectAsFunction
While remote calls usedinspectAsFunction, local calls just used theatomValue, which meant names that needed to be unquoted weren't and caused parsing errors.
- Decompile local function calls in Elixir DbgI using inspectAsFunction
- Erlang Abst
- Decompile Erlang Abst string with OtpErlangList as strings with non-ASCII codepoints
Fixes unknown string format inidna.beam - Always group for comprehensions in sequence even if there is only 1 element
Some forms offorcomprehensions cannot be used as the sole argument of a call unless surrounded by parentheses, so always add those parentheses. - Decompile Erlang Abst record empty record fields as
[]for updates - Decompile Erlang Abst
left xor rightas:erlang.xor(left, right)
Elixir does not have a logical xor infix operator, so have to decompile as normal function call - Decompile Erlang Abst named anonymous function as a macro
Named anonymous functions are support in Erlang, but not Elixir, so fake it as a macro when decompiling. - Add builtin-types for Erlang Abst
bitstringfloatnonempty_improper_listnonempty_maybe_improper_list
- Decompile tagged atoms and other complex expression as function name in Abst capture
- Decompile Erlang Abst
float - Decompile Erlang Abst
beginblocks as parenthesized groups separated by ; - Decompile empty OtpErlangList as "" in Erlang Abst
string - Track if decompiled Erlang Abst contains do blocks so that they can be surrounded by parentheses when necessary
- Fix decompiling Erlang Abst
record_indexwhen record name needs to be unquoted - Decompile
mapupdates in Erlang Abst - Erlang Abst Function capture names are OtpErlangAtom and not tagged Atoms
- Inspect local function names as atoms instead of as functions when apply/3 is used for operations and unquoted in Erlang Abst
Stops:unquote(:"NAME")from happening - Surround anonymous function definitions that are called immediately with parentheses and call arguments with .( in Erlang Abst
- Decompile
field_typein Erlang Abst
Fixes decompilinghipe_icode_call_elim.beam - Inspect type name usages as local functions to ensure invalid names are unquoted
- Inspect type names as local functions to ensure invalid names are unquoted
- Decompile Erlang Abst string with OtpErlangList as strings with non-ASCII codepoints
- Elixir
- References
- Stop looking for qualifiers to prepend when exiting interpolation
- Don't safeMultiResolve null call.reference in resolvesToModularName
- Types
- Fix highlighting types declared with unquote and no secondary parentheses
- Performance
- Don't error if a private function mirror cannot be found
Private functions are not decompiled if there are too many public functions. - Fix CallDefinitionImpl.isExported
Used to be hard-coded to returntrue, but this pre-dated decompiling private functions. Now with decompiling private functions, isExported needs to defer to theDefinitionand count as unexported if a private function, macro, or guard.
- Don't error if a private function mirror cannot be found
- Decompiler
- #2337 - @KronicDeth
- Walk map constructin arguments, associatons, and variables when resolving type parameters.
- #2339 - @KronicDeth
- Don't use
PluginId.findIdthat doesn't exist in 2021.1.X
- Don't use