Skip to content

Commit

Permalink
LSP: Collect declaration tokens from std lib prelude and dependencies (
Browse files Browse the repository at this point in the history
…FuelLabs#3212)

We weren't collecting tokens imported by default with the new std lib
prelude. This PR now collects these tokens so we can do things like show
documentation for imported types on hover requests.

closes FuelLabs#3208
  • Loading branch information
JoshuaBatty authored Nov 4, 2022
1 parent 5f29c66 commit d818f14
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 104 deletions.
38 changes: 24 additions & 14 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2458,18 +2458,22 @@ fn update_json_type_declaration(
}
}

/// Compile the entire forc package and return a typed program, if any.
pub fn check(
plan: &BuildPlan,
terse_mode: bool,
) -> anyhow::Result<CompileResult<(ParseProgram, Option<ty::TyProgram>)>> {
/// A `CompileResult` thats type is a tuple containing a `ParseProgram` and `Option<ty::TyProgram>`
type ParseAndTypedPrograms = CompileResult<(ParseProgram, Option<ty::TyProgram>)>;

/// Compile the entire forc package and return the parse and typed programs
/// of the dependancies and project.
/// The final item in the returned vector is the project.
pub fn check(plan: &BuildPlan, terse_mode: bool) -> anyhow::Result<Vec<ParseAndTypedPrograms>> {
//TODO remove once type engine isn't global anymore.
sway_core::clear_lazy_statics();
let mut lib_namespace_map = Default::default();
let mut source_map = SourceMap::new();
// During `check`, we don't compile so this stays empty.
let compiled_contract_deps = HashMap::new();
for (i, &node) in plan.compilation_order.iter().enumerate() {

let mut results = vec![];
for &node in plan.compilation_order.iter() {
let pkg = &plan.graph[node];
let manifest = &plan.manifest_map()[&pkg.id()];
let constants = manifest.config_time_constants();
Expand All @@ -2488,7 +2492,10 @@ pub fn check(
} = parse(manifest, terse_mode)?;

let parse_program = match value {
None => return Ok(CompileResult::new(None, warnings, errors)),
None => {
results.push(CompileResult::new(None, warnings, errors));
return Ok(results);
}
Some(program) => program,
};

Expand All @@ -2499,7 +2506,8 @@ pub fn check(
let typed_program = match ast_result.value {
None => {
let value = Some((parse_program, None));
return Ok(CompileResult::new(value, warnings, errors));
results.push(CompileResult::new(value, warnings, errors));
return Ok(results);
}
Some(typed_program) => typed_program,
};
Expand All @@ -2510,13 +2518,15 @@ pub fn check(

source_map.insert_dependency(manifest.dir());

// We only need to return the final `TypedProgram`.
if i == plan.compilation_order.len() - 1 {
let value = Some((parse_program, Some(typed_program)));
return Ok(CompileResult::new(value, warnings, errors));
}
let value = Some((parse_program, Some(typed_program)));
results.push(CompileResult::new(value, warnings, errors));
}
bail!("unable to check sway program: build plan contains no packages")

if results.is_empty() {
bail!("unable to check sway program: build plan contains no packages")
}

Ok(results)
}

/// Returns a parsed AST from the supplied [PackageManifestFile]
Expand Down
4 changes: 3 additions & 1 deletion forc-plugins/forc-doc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ pub fn main() -> Result<()> {

// compile the program and extract the docs
let plan = pkg::BuildPlan::from_lock_and_manifest(&manifest, locked, offline)?;
let compilation = pkg::check(&plan, silent)?;
let compilation = pkg::check(&plan, silent)?
.pop()
.expect("there is guaranteed to be at least one elem in the vector");
let raw_docs: Documentation = Document::from_ty_program(&compilation, no_deps)?;
// render docs to HTML
let rendered_docs: RenderedDocumentation =
Expand Down
7 changes: 6 additions & 1 deletion forc/src/ops/forc_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,10 @@ pub fn check(command: CheckCommand) -> Result<CompileResult<ty::TyProgram>> {
let manifest = PackageManifestFile::from_dir(&this_dir)?;
let plan = pkg::BuildPlan::from_lock_and_manifest(&manifest, locked, offline)?;

Ok(pkg::check(&plan, terse_mode)?.flat_map(|(_, tp)| CompileResult::new(tp, vec![], vec![])))
let mut v = pkg::check(&plan, terse_mode)?;
let res = v
.pop()
.expect("there is guaranteed to be at least one elem in the vector")
.flat_map(|(_, tp)| CompileResult::new(tp, vec![], vec![]));
Ok(res)
}
2 changes: 1 addition & 1 deletion sway-core/src/language/parsed/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use sway_types::span::Span;

/// Represents some exportable information that results from compiling some
/// Sway source code.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ParseTree {
/// The untyped AST nodes that constitute this tree's root nodes.
pub root_nodes: Vec<AstNode>,
Expand Down
4 changes: 2 additions & 2 deletions sway-core/src/language/parsed/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::ParseTree;
use sway_types::Ident;

/// A module and its submodules in the form of a tree.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ParseModule {
/// The content of this module in the form of a `ParseTree`.
pub tree: ParseTree,
Expand All @@ -15,7 +15,7 @@ pub struct ParseModule {
/// A library module that was declared as a `dep` of another module.
///
/// Only submodules are guaranteed to be a `library` and have a `library_name`.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ParseSubmodule {
/// The name of a submodule, parsed from the `library` declaration within the module itself.
pub library_name: Ident,
Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/language/parsed/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use sway_types::Ident;
/// A parsed, but not yet type-checked, Sway program.
///
/// Includes all modules in the form of a `ParseModule` tree accessed via the `root`.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ParseProgram {
pub kind: TreeType,
pub root: ParseModule,
Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/language/ty/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
type_system::*,
};

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct TyProgram {
pub kind: TyProgramKind,
pub root: TyModule,
Expand Down
68 changes: 68 additions & 0 deletions sway-lsp/src/core/dependency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use crate::{
core::token::{AstToken, SymbolKind, Token, TokenMap, TypeDefinition, TypedAstToken},
utils::token::to_ident_key,
};
use sway_core::{
declaration_engine as de,
language::{
parsed::{AstNode, AstNodeContent, Declaration},
ty,
},
};
use sway_types::Spanned;

/// Insert Declaration tokens into the TokenMap.
pub fn collect_parsed_declaration(node: &AstNode, tokens: &TokenMap) {
if let AstNodeContent::Declaration(declaration) = &node.content {
let parsed_token = AstToken::Declaration(declaration.clone());

let (ident, symbol_kind) = match declaration {
Declaration::VariableDeclaration(variable) => {
(variable.name.clone(), SymbolKind::Variable)
}
Declaration::StructDeclaration(decl) => (decl.name.clone(), SymbolKind::Struct),
Declaration::TraitDeclaration(decl) => (decl.name.clone(), SymbolKind::Trait),
Declaration::FunctionDeclaration(decl) => (decl.name.clone(), SymbolKind::Function),
Declaration::ConstantDeclaration(decl) => (decl.name.clone(), SymbolKind::Const),
Declaration::EnumDeclaration(decl) => (decl.name.clone(), SymbolKind::Enum),
_ => return,
};

let key = to_ident_key(&ident);
let token = Token::from_parsed(parsed_token, symbol_kind);
tokens.insert(key, token);
}
}

/// Insert TypedDeclaration tokens into the TokenMap.
pub fn collect_typed_declaration(node: &ty::TyAstNode, tokens: &TokenMap) {
if let ty::TyAstNodeContent::Declaration(declaration) = &node.content {
let typed_token = TypedAstToken::TypedDeclaration(declaration.clone());

if let Ok(ident) = match declaration {
ty::TyDeclaration::VariableDeclaration(variable) => Ok(variable.name.clone()),
ty::TyDeclaration::StructDeclaration(decl_id) => {
de::de_get_struct(decl_id.clone(), &declaration.span()).map(|decl| decl.name)
}
ty::TyDeclaration::TraitDeclaration(decl_id) => {
de::de_get_trait(decl_id.clone(), &declaration.span()).map(|decl| decl.name)
}
ty::TyDeclaration::FunctionDeclaration(decl_id) => {
de::de_get_function(decl_id.clone(), &declaration.span()).map(|decl| decl.name)
}
ty::TyDeclaration::ConstantDeclaration(decl_id) => {
de::de_get_constant(decl_id.clone(), &declaration.span()).map(|decl| decl.name)
}
ty::TyDeclaration::EnumDeclaration(decl_id) => {
de::de_get_enum(decl_id.clone(), &declaration.span()).map(|decl| decl.name)
}
_ => return,
} {
let ident = to_ident_key(&ident);
if let Some(mut token) = tokens.get_mut(&ident) {
token.typed = Some(typed_token);
token.type_def = Some(TypeDefinition::Ident(ident.0));
}
}
}
}
1 change: 1 addition & 0 deletions sway-lsp/src/core/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub(crate) mod config;
pub(crate) mod dependency;
pub mod document;
pub mod session;
pub(crate) mod token;
Expand Down
Loading

0 comments on commit d818f14

Please sign in to comment.