Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
- Made GetKnownContexts custom request not wait for analysis results by default, reducing the chances of overfilling workpool capacity while analysises are running.
- Fix issue where the server would internally format URIs incorrectly in some cases
- The language server logs will now be in local time of whatever machine they are running on, rather than UTC.
- Fixed bug in template cycle detection that could cause crashes when an object
instantiated a template involved in a cycle
- Fixed template cycle breaking so it will be deterministic in all cases
- Added basic type system. Goto definition/declaration and find
references will now work on types. Goto type-def will work on simple references
- Fixed internal error when `vect` modifiers were made inside type declaration

## 0.9.19
- Added configuration option to control the max cache size while resolving references in semantic analysis, defaulting to 500MB
Expand Down
59 changes: 58 additions & 1 deletion src/actions/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::actions::{AnalysisProgressKind, AnalysisWaitKind,
ContextDefinition, InitActionContext,
rpc_error_code};
use crate::actions::notifications::ContextDefinitionKindParam;
use crate::actions::semantic_lookup::{DLSLimitation, declarations_at_fp, definitions_at_fp, implementations_at_fp, references_at_fp};
use crate::actions::semantic_lookup::{DLSLimitation, declarations_at_fp, definitions_at_fp, implementations_at_fp, references_at_fp, type_definitions_at_fp};
use crate::analysis::{Named, DeclarationSpan, LocationSpan};
use crate::analysis::symbols::SimpleSymbol;
use crate::config::Config;
Expand All @@ -31,6 +31,7 @@ pub use crate::lsp_data::request::{
Formatting,
GotoDeclaration, GotoDeclarationResponse,
GotoDefinition,
GotoTypeDefinition,
GotoImplementation, GotoImplementationResponse,
HoverRequest,
RangeFormatting,
Expand Down Expand Up @@ -570,6 +571,62 @@ impl RequestAction for GotoDefinition {
}
}

impl RequestAction for GotoTypeDefinition {
type Response = ResponseWithMessage<Option<GotoDefinitionResponse>>;

fn timeout() -> std::time::Duration {
crate::server::dispatch::DEFAULT_REQUEST_TIMEOUT * 5
}

fn fallback_response() -> Result<Self::Response, ResponseError> {
Ok(None.into())
}

fn get_identifier(params: &Self::Params) -> String {
Self::request_identifier(
&text_document_position_to_ident(
&params.text_document_position_params))
}

fn handle<O: Output>(
ctx: InitActionContext<O>,
params: Self::Params,
) -> Result<Self::Response, ResponseError> {
debug!("Requesting type definitions with params {:?}", params);
let fp = {
let maybe_fp = ctx.text_doc_pos_to_pos(
&params.text_document_position_params,
"goto_type_def");
if maybe_fp.is_none() {
return Self::fallback_response();
}
maybe_fp.unwrap()
};
let canon_path = make_canon_path!(fp.path())?;
wait_for_device_path!(ctx, canon_path);

let mut limitations = HashSet::new();
match type_definitions_at_fp(&ctx, &fp, &mut limitations) {
Ok(locs) => {
let lsp_locations: Vec<_> = locs.into_iter()
.map(|l|ls_util::dls_to_location(&l))
.collect();
Ok(response_maybe_with_limitations(
&fp.path(),
Some(GotoDefinitionResponse::Array(lsp_locations)),
limitations,
&ctx))
},
Err(lookuperror) => {
let main_file_name = fp.path();
warn_miss_lookup(lookuperror,
main_file_name.to_str());
Self::fallback_response()
},
}
}
}

impl RequestAction for References {
type Response = ResponseWithMessage<Vec<Location>>;

Expand Down
142 changes: 110 additions & 32 deletions src/actions/semantic_lookup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::analysis::symbols::DMLSymbolKind;
use crate::analysis::{DeviceAnalysis, IsolatedAnalysis, LocationSpan, SymbolRef, ZeroRange};

use crate::analysis::parsing::tree::{ZeroSpan, ZeroFilePosition};
use crate::analysis::reference::{CodeReference, Reference, ReferenceKind};
use crate::analysis::reference::{CodeReference, Reference};
use crate::file_management::CanonPath;
use crate::server::Output;

Expand All @@ -42,14 +42,6 @@ impl fmt::Display for DLSLimitation {
}
}

pub fn type_semantic_limitation() -> DLSLimitation {
DLSLimitation {
issue_num: 65,
description: "The DLS does not currently support semantic analysis of \
types, including reference finding".to_string(),
}
}

pub fn isolated_template_limitation(template_name: &str) -> DLSLimitation {
DLSLimitation {
issue_num: 31,
Expand All @@ -62,10 +54,6 @@ pub fn isolated_template_limitation(template_name: &str) -> DLSLimitation {
}
}

// Because symbols need to be tied to their source analysis that result is
// a [(DeviceAnalysis, [SymbolRef])] list
// The reference comes from an isolated analysis, and thus is disconnected from a device
// context
type DeviceSymbols<'t> = Vec<(&'t DeviceAnalysis, Vec<SymbolRef>)>;
enum SymbolsOrReference<'t> {
Symbols(DeviceSymbols<'t>),
Expand Down Expand Up @@ -182,32 +170,28 @@ fn get_refs_and_syms_at_fp<'t>(
-> Result<SymbolsOrReference<'t>, AnalysisLookupError> {
debug!("Looking up references and symbols at position {:?}", fp);
let ref_at_pos = analysis_info.isolated_analysis.lookup_reference(fp);

let context_sym_at_pos = context_symbol_at_pos(analysis_info.isolated_analysis, fp);
let symbols_at_fp = context_sym_at_pos.map(|cs| {
analysis_info.device_analysises.iter().map(
|a|(*a, match a.lookup_symbols(&cs, relevant_limitations) {
Ok(syms) => syms,
Err(e) => {
internal_error!("failed to find context symbol at {:?}: {}", fp, e);
vec![]
}
}))
.collect::<Vec<_>>()
});
if let Some(syms) = symbols_at_fp {

if let Some(decl) = declaration_at_pos(fp, analysis_info) {
if ref_at_pos.is_some() {
error!("Obtained both symbol and reference at {:?}\
(reference is {:?}), defaulted to symbol",
&fp, ref_at_pos);
}
let syms = match decl {
PositionDeclaration::Contexted(cs) =>
analysis_info.device_analysises.iter().map(
|a|(*a, match a.lookup_symbols(&cs, relevant_limitations) {
Ok(syms) => syms,
Err(e) => {
internal_error!("failed to find context symbol at {:?}: {}", fp, e);
vec![]
}
}))
.collect::<Vec<_>>(),
PositionDeclaration::TypeMember(syms) => syms,
};
return Ok(SymbolsOrReference::Symbols(syms));
}
if let Some(refr) = ref_at_pos.and_then(|r|r.as_code_ref()) {
if refr.reference_kind() == ReferenceKind::Type {
relevant_limitations.insert(type_semantic_limitation());
}
}
if let Some(refr) = ref_at_pos {
Ok(SymbolsOrReference::Reference(refr.clone()))
} else {
Expand Down Expand Up @@ -285,6 +269,26 @@ fn context_symbol_at_pos<'t>(isolated_analysis: &'t IsolatedAnalysis, pos: &Zero
context
}

enum PositionDeclaration<'t> {
Contexted(ContextedSymbol<'t>),
TypeMember(DeviceSymbols<'t>),
}

fn declaration_at_pos<'t>(fp: &ZeroFilePosition, analysis_info: &AnalysisInfo<'t>)
-> Option<PositionDeclaration<'t>> {
if let Some(cs) = context_symbol_at_pos(analysis_info.isolated_analysis, fp) {
return Some(PositionDeclaration::Contexted(cs));
}
let member_syms: DeviceSymbols<'t> = analysis_info.device_analysises.iter()
.map(|a|(*a, a.member_symbol_at_pos(fp, &analysis_info.isolated_analysis.ast)
.into_iter().collect()))
.collect();
if member_syms.iter().any(|(_, syms)|!syms.is_empty()) {
return Some(PositionDeclaration::TypeMember(member_syms));
}
None
}

fn symbol_implementations_of_symbol<'t>(symbol: &'t SymbolRef,
analysis: &'t DeviceAnalysis)
-> Vec<SymbolRef> {
Expand Down Expand Up @@ -459,3 +463,77 @@ pub fn references_at_fp(context: &InitActionContext<impl Output>,
.flat_map(|s|s.lock().unwrap().references.clone())
.collect())
}

/// Walk a resolved `DMLType`, peeling pointer/array/vector wrappers and
/// typedef indirections, and return the span of the outermost
/// typedef/template-as-type declaration, if any.
fn outermost_typedef_span(ty: &crate::analysis::templating::types::DMLType)
-> Option<ZeroSpan> {
use crate::analysis::templating::types::DMLConcreteType;
let mut cur = ty.clone();
let mut outermost = None;
loop {
let concrete = cur?;
match concrete.as_ref() {
DMLConcreteType::Typedef(td) => { outermost.get_or_insert(td.decl_name.span); }
DMLConcreteType::Trait(t) => { outermost.get_or_insert(t.decl_name.span); }
_ => {}
}
cur = match concrete.peel_one() {
Some(next) => next.clone(),
None => return outermost,
};
}
}

/// Resolve the `DMLType` referred to by a symbol's source, if any.
fn resolved_type_from_symbol_source(source: &crate::analysis::symbols::SymbolSource)
-> Option<&crate::analysis::templating::types::DMLType> {
source.resolved_type()
}


pub fn type_definitions_at_fp(context: &InitActionContext<impl Output>,
fp: &ZeroFilePosition,
relevant_limitations: &mut HashSet<DLSLimitation>)
-> Result<Vec<ZeroSpan>, AnalysisLookupError> {
let analysis_lock = context.analysis.lock().unwrap();
let mut semantic_lookup = SemanticLookup::create_lookup(
fp,
&analysis_lock,
context)?;
mem::swap(relevant_limitations, &mut semantic_lookup.recognized_limitations);

let mut results: Vec<ZeroSpan> = Vec::new();
for (_device, symbols) in &semantic_lookup.stored_symbols {
for sym in symbols {
let (kind, source) = {
let lock = sym.lock().unwrap();
(lock.kind, lock.source.clone())
};
match kind {
// A typedef is its own type-definition.
DMLSymbolKind::Typedef => {
let lock = sym.lock().unwrap();
results.extend(lock.definitions.iter().copied());
}
// For variables and method args, walk the resolved type.
DMLSymbolKind::Saved
| DMLSymbolKind::Session
| DMLSymbolKind::Extern
| DMLSymbolKind::Local
| DMLSymbolKind::Constant
| DMLSymbolKind::MethodArg => {
if let Some(ty) = resolved_type_from_symbol_source(&source) {
if let Some(span) = outermost_typedef_span(ty) {
results.push(span);
}
}
}
_ => {}
}
}
}
Ok(results)
}

Loading
Loading