Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: e1cd2ccd4060
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: d14ad6727a3c
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Nov 24, 2015

  1. compiler.embedding: show suggestions for mistyped host object attribu…

    …tes.
    whitequark committed Nov 24, 2015
    Copy the full SHA
    14993e8 View commit details
  2. Copy the full SHA
    d14ad67 View commit details
49 changes: 32 additions & 17 deletions artiq/compiler/embedding.py
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
from pythonparser import ast, algorithm, source, diagnostic, parse_buffer
from pythonparser import lexer as source_lexer, parser as source_parser

from Levenshtein import jaro_winkler
from Levenshtein import ratio as similarity, jaro_winkler

from ..language import core as language_core
from . import types, builtins, asttyped, prelude
@@ -207,6 +207,13 @@ def assign_attribute(self, obj, attr_name, value):
return ast.Assign(targets=[attr_node], value=value_node,
op_locs=[equals_loc], loc=name_loc.join(value_node.loc))


def suggest_identifier(id, names):
sorted_names = sorted(names, key=lambda other: jaro_winkler(id, other), reverse=True)
if len(sorted_names) > 0:
if jaro_winkler(id, sorted_names[0]) > 0.0 and similarity(id, sorted_names[0]) > 0.5:
return sorted_names[0]

class StitchingASTTypedRewriter(ASTTypedRewriter):
def __init__(self, engine, prelude, globals, host_environment, quote):
super().__init__(engine, prelude)
@@ -227,7 +234,12 @@ def visit_Name(self, node):
if node.id in self.host_environment:
return self.quote(self.host_environment[node.id], node.loc)
else:
suggestion = self._most_similar_ident(node.id)
names = set()
names.update(self.host_environment.keys())
for typing_env in reversed(self.env_stack):
names.update(typing_env.keys())

suggestion = suggest_identifier(node.id, names)
if suggestion is not None:
diag = diagnostic.Diagnostic("fatal",
"name '{name}' is not bound to anything; did you mean '{suggestion}'?",
@@ -240,17 +252,6 @@ def visit_Name(self, node):
node.loc)
self.engine.process(diag)

def _most_similar_ident(self, id):
names = set()
names.update(self.host_environment.keys())
for typing_env in reversed(self.env_stack):
names.update(typing_env.keys())

sorted_names = sorted(names, key=lambda other: jaro_winkler(id, other), reverse=True)
if len(sorted_names) > 0:
if jaro_winkler(id, sorted_names[0]) > 0.0:
return sorted_names[0]

class StitchingInferencer(Inferencer):
def __init__(self, engine, value_map, quote):
super().__init__(engine)
@@ -277,13 +278,27 @@ def visit_AttributeT(self, node):
# which would be the optimal solution.
for object_value, object_loc in self.value_map[object_type]:
if not hasattr(object_value, node.attr):
if node.attr.startswith('_'):
names = set(filter(lambda name: not name.startswith('_'),
dir(object_value)))
else:
names = set(dir(object_value))
suggestion = suggest_identifier(node.attr, names)

note = diagnostic.Diagnostic("note",
"attribute accessed here", {},
node.loc)
diag = diagnostic.Diagnostic("error",
"host object does not have an attribute '{attr}'",
{"attr": node.attr},
object_loc, notes=[note])
if suggestion is not None:
diag = diagnostic.Diagnostic("error",
"host object does not have an attribute '{attr}'; "
"did you mean '{suggestion}'?",
{"attr": node.attr, "suggestion": suggestion},
object_loc, notes=[note])
else:
diag = diagnostic.Diagnostic("error",
"host object does not have an attribute '{attr}'",
{"attr": node.attr},
object_loc, notes=[note])
self.engine.process(diag)
return

6 changes: 6 additions & 0 deletions lit-test/test/embedding/device_db.pyon
Original file line number Diff line number Diff line change
@@ -4,5 +4,11 @@
"module": "artiq.coredevice.comm_dummy",
"class": "Comm",
"arguments": {}
},
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {"ref_period": 1e-9}
}
}
16 changes: 16 additions & 0 deletions lit-test/test/embedding/error_attr_absent_suggest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t

from artiq.language.core import *
from artiq.language.types import *

class c:
xx = 1

@kernel
def entrypoint():
# CHECK-L: <synthesized>:1: error: host object does not have an attribute 'x'; did you mean 'xx'?
# CHECK-L: ${LINE:+1}: note: expanded from here
a = c
# CHECK-L: ${LINE:+1}: note: attribute accessed here
a.x
10 changes: 10 additions & 0 deletions lit-test/test/embedding/error_name_absent_suggest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t

from artiq.language.core import *
from artiq.language.types import *

@kernel
def entrypoint():
# CHECK-L: ${LINE:+1}: fatal: name 'prnt' is not bound to anything; did you mean 'print'?
prnt()