Skip to content
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
- Linting warnings can now annotate their messages with which linting rule enforces it. You can turn this off by setting 'annotate_lints' to 'false' in your lint config file
- Minor fixes to the description of linting messages
- Fixed an issue where diagnostics would sometime be reported within the wrong file
- Corrected incorrect parsing around tuple-declared 'local' variables in for-loops
- Added the ability to declare 'saved' or 'session' variables in for-loops
- Added functionality for goto-def/decl/ref to or from variables declared
within a function scope.

## 0.9.11
- Fixed deadlock when a configuration update happens
Expand Down
632 changes: 518 additions & 114 deletions src/analysis/mod.rs

Large diffs are not rendered by default.

31 changes: 16 additions & 15 deletions src/analysis/parsing/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::analysis::parsing::expression::{Expression,
MemberLiteralContent,
ExpressionContent,
ParenExpressionContent};
use crate::analysis::parsing::misc::{Initializer, InitializerContent, CDecl,
use crate::analysis::parsing::misc::{Initializer, InitializerContent,
SingleInitializerContent,
ident_filter, objident_filter};
use crate::analysis::parsing::structure::{parse_vardecl, VarDecl};
Expand Down Expand Up @@ -661,28 +661,28 @@ impl TreeElement for ForPostElement {

#[derive(Debug, Clone, PartialEq)]
pub enum ForPre {
Local(LeafToken, CDecl, Option<(LeafToken, Initializer)>),
Declaration(LeafToken, VarDecl, Option<(LeafToken, Initializer)>),
Post(ForPost),
}

impl TreeElement for ForPre {
fn range(&self) -> ZeroRange {
match self {
Self::Local(loc, cdecl, init) =>
Range::combine(Range::combine(loc.range(), cdecl.range()),
Self::Declaration(loc, vardecls, init) =>
Range::combine(Range::combine(loc.range(), vardecls.range()),
init.range()),
Self::Post(forpost) => forpost.range(),
}
}
fn subs(&self) -> TreeElements<'_> {
match self {
Self::Local(loc, cdecl, init) =>
create_subs!(loc, cdecl, init),
Self::Declaration(loc, vardecls, init) =>
create_subs!(loc, vardecls, init),
Self::Post(forpost) => create_subs!(forpost),
}
}
fn post_parse_sanity(&self, _file: &TextFile) -> Vec<LocalDMLError> {
if let ForPre::Local(_, cdecl, _) = self {
if let ForPre::Declaration(_, cdecl, _) = self {
cdecl.ensure_named()
} else {
vec![]
Expand Down Expand Up @@ -814,18 +814,19 @@ fn parse_forpost(context: &ParseContext, stream: &mut FileParser<'_>, file_info:
fn parse_forpre(context: &ParseContext, stream: &mut FileParser<'_>, file_info: &FileInfo) -> ForPre {
let mut new_context = context.enter_context(doesnt_understand_tokens);
match new_context.peek_kind(stream) {
Some(TokenKind::Local) => {
let local = new_context.expect_next_kind(stream, TokenKind::Local);
let decl = CDecl::parse(&new_context, stream, file_info);
Some(TokenKind::Local | TokenKind::Saved | TokenKind::Session) => {
let decl_kind = new_context.next_leaf(stream);
let decls = parse_vardecl(&new_context, stream, file_info);
let initializer = match new_context.peek_kind(stream) {
Some(TokenKind::Assign) => {
let assign = new_context.next_leaf(stream);
let init = Initializer::parse(&new_context, stream, file_info);
Some((assign, init))
let equals = new_context.next_leaf(stream);
let init = Initializer::parse(
&new_context, stream, file_info);
Some((equals, init))
},
_ => None
_ => None,
};
ForPre::Local(local, decl, initializer)
ForPre::Declaration(decl_kind, decls, initializer)
},
_ => ForPre::Post(parse_forpost(&new_context, stream, file_info)),
}
Expand Down
60 changes: 41 additions & 19 deletions src/analysis/structure/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,10 +996,6 @@ pub struct Method {
pub references: Vec<Reference>,
}

impl SymbolContainer for Method {
fn symbols(&self) -> Vec<&dyn StructureSymbol> { vec![] }
}

impl DeclarationSpan for Method {
fn span(&self) -> &ZeroSpan {
self.object.span()
Expand All @@ -1023,7 +1019,9 @@ impl Scope for Method {
ContextKey::Structure(SimpleSymbol::make(self, self.kind()))
}
fn defined_symbols(&self) -> Vec<&dyn StructureSymbol> {
self.arguments.to_symbols()
let mut symbols = self.arguments.to_symbols();
symbols.append(&mut self.body.symbols());
symbols
}
fn defined_scopes(&self) -> Vec<&dyn Scope> {
vec![]
Expand Down Expand Up @@ -1345,19 +1343,20 @@ impl DeclarationSpan for Variable {
}
}

fn to_variable_structure<'a>(content: &structure::VariableContent,
kind: VariableDeclKind,
report: &mut Vec<LocalDMLError>,
file: FileSpec<'a>) -> Option<Variable> {
let values = if let Some((_, init_ast)) = &content.initializer {
pub fn to_variable_structure<'a>(decls: &structure::VarDecl,
initializer: Option<&misc::Initializer>,
kind: VariableDeclKind,
report: &mut Vec<LocalDMLError>,
file: FileSpec<'a>) -> Option<Variable> {
let values = if let Some(init_ast) = initializer {
init_ast.with_content(
|con|to_initializer(con, report, file),
vec![])
} else {
vec![]
};
// I _think_ we will always have a real content here
let vars = match &content.cdecl {
let vars = match decls {
structure::VarDecl::One(decl) => {
vec![to_variable_decl_structure(decl, kind, report, file)?]
},
Expand All @@ -1369,11 +1368,24 @@ fn to_variable_structure<'a>(content: &structure::VariableContent,
}
};

if let Some((assign, init_ast)) = &content.initializer {
if vars.len() != values.len() {
if let Some(init) = initializer {
// NOTE: this is the half of the initializer arity that
// can be performed in an isolated context. Checking that
// the method would return the right number of values
// is performed in device analysis
// TODO: if we ever update to latest rust, we can merge this if-case
// with the next
let mut is_single_fun = false;

if let [val] = values.as_slice() {
if let InitializerKind::One(expr) = &val.kind {
is_single_fun = matches!(
expr.as_ref(), ExpressionKind::FunctionCall(_));
}
}
if !is_single_fun && vars.len() != values.len(){
report.push(LocalDMLError {
range: init_ast.with_content(
|i|i.range(), assign.range()),
range: init.range(),
description: "Wrong number of \
initializers in declaration".to_string(),
});
Expand All @@ -1383,7 +1395,11 @@ fn to_variable_structure<'a>(content: &structure::VariableContent,
Some(Variable {
vars,
values,
span: ZeroSpan::from_range(content.range(), file.path),
span: ZeroSpan::from_range(
initializer.map_or_else(
||decls.range(),
|i|ZeroRange::combine(decls.range(), i.range())),
file.path),
})
}

Expand Down Expand Up @@ -1578,7 +1594,9 @@ fn to_objectstatement<'a>(content: &structure::DMLObjectContent,
Export::to_structure(con, report, file)?))),
structure::DMLObjectContent::Extern(con) =>
DMLStatementKind::Statement(DMLStatement::Object(DMLObject::Extern(
to_variable_structure(con, VariableDeclKind::Extern,
to_variable_structure(&con.cdecl,
con.initializer.as_ref().map(|(_,i)|i),
VariableDeclKind::Extern,
report, file)?))),
structure::DMLObjectContent::Event(con) =>
DMLStatementKind::Statement(DMLStatement::Object(
Expand Down Expand Up @@ -1650,11 +1668,15 @@ fn to_objectstatement<'a>(content: &structure::DMLObjectContent,
to_register(con, report, file)?))),
structure::DMLObjectContent::Saved(con) =>
DMLStatementKind::Statement(DMLStatement::Object(DMLObject::Saved(
to_variable_structure(con, VariableDeclKind::Saved,
to_variable_structure(&con.cdecl,
con.initializer.as_ref().map(|(_,i)|i),
VariableDeclKind::Saved,
report, file)?))),
structure::DMLObjectContent::Session(con) =>
DMLStatementKind::Statement(DMLStatement::Object(DMLObject::Session(
to_variable_structure(con, VariableDeclKind::Session,
to_variable_structure(&con.cdecl,
con.initializer.as_ref().map(|(_,i)|i),
VariableDeclKind::Session,
report, file)?))),
structure::DMLObjectContent::Subdevice(con) =>
DMLStatementKind::Statement(DMLStatement::Object(
Expand Down
Loading