Skip to content

Commit

Permalink
feat: adds detection of duplicate definition names
Browse files Browse the repository at this point in the history
  • Loading branch information
baszalmstra committed Oct 5, 2019
1 parent 48e7b0b commit 1a3a3a3
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 7 deletions.
2 changes: 1 addition & 1 deletion crates/mun/test/main.mun
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ fn multiply(a:float, b:float):float {

fn main():int {
5
}
}
16 changes: 16 additions & 0 deletions crates/mun_compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::sync::{Arc, Mutex};
use termcolor::{ColorChoice, StandardStream};

pub use mun_codegen::OptimizationLevel;
use mun_syntax::{ast, SyntaxKind};
use mun_target::spec;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -154,6 +155,21 @@ fn diagnostics(db: &CompilerDatabase, file_id: FileId) -> Vec<Diagnostic> {
d.found.display(db)
),
});
})
.on::<mun_hir::diagnostics::DuplicateDefinition, _>(|d| {
result.borrow_mut().push(Diagnostic {
level: Level::Error,
loc: match d.definition.kind() {
SyntaxKind::FUNCTION_DEF => {
ast::FunctionDef::cast(d.definition.to_node(&parse.tree().syntax()))
.map(|f| f.signature_range())
.unwrap_or(d.highlight_range())
.into()
}
_ => d.highlight_range().into(),
},
message: d.message(),
});
});

if let Some(module) = Module::package_modules(db)
Expand Down
68 changes: 63 additions & 5 deletions crates/mun_hir/src/code_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ impl Module {
}

pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) {
for diag in db.module_data(self.file_id).diagnostics.iter() {
diag.add_to(db, self, sink);
}
for decl in self.declarations(db) {
match decl {
ModuleDef::Function(f) => f.diagnostics(db, sink),
Expand All @@ -54,6 +57,7 @@ impl Module {
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
pub struct ModuleData {
definitions: Vec<ModuleDef>,
diagnostics: Vec<ModuleDefinitionDiagnostic>
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
Expand All @@ -66,13 +70,25 @@ impl ModuleData {
let items = db.raw_items(file_id);
let mut data = ModuleData::default();
let loc_ctx = LocationCtx::new(db, file_id);
let mut definition_by_name = FxHashMap::default();
for item in items.items().iter() {
match item {
RawFileItem::Definition(def) => match items[*def].kind {
DefKind::Function(ast_id) => {
data.definitions.push(ModuleDef::Function(Function {
id: FunctionId::from_ast_id(loc_ctx, ast_id),
}))
RawFileItem::Definition(def) => {
if let Some(prev_definition) = definition_by_name.get(&items[*def].name) {
data.diagnostics.push(diagnostics::ModuleDefinitionDiagnostic::DuplicateName {
name: items[*def].name.clone(),
definition: *def,
first_definition: *prev_definition
})
} else {
definition_by_name.insert(items[*def].name.clone(), *def);
}
match items[*def].kind {
DefKind::Function(ast_id) => {
data.definitions.push(ModuleDef::Function(Function {
id: FunctionId::from_ast_id(loc_ctx, ast_id),
}))
}
}
},
};
Expand Down Expand Up @@ -258,10 +274,52 @@ pub enum BuiltinType {
}

use crate::name::*;
use crate::code_model::diagnostics::ModuleDefinitionDiagnostic;

impl BuiltinType {
#[rustfmt::skip]
pub(crate) const ALL: &'static [(Name, BuiltinType)] = &[
(FLOAT, BuiltinType::Float),
(INT, BuiltinType::Int),
];
}

mod diagnostics {
use crate::{Name, DefDatabase};
use crate::raw::{DefId, DefKind};
use crate::diagnostics::{DiagnosticSink, DuplicateDefinition};
use super::Module;
use mun_syntax::{SyntaxNodePtr, AstNode};

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub(super) enum ModuleDefinitionDiagnostic {
DuplicateName { name: Name, definition: DefId, first_definition: DefId },
}

fn syntax_ptr_from_def(db: &impl DefDatabase, owner: Module, kind: DefKind) -> SyntaxNodePtr {
match kind {
DefKind::Function(id) => SyntaxNodePtr::new(id.with_file_id(owner.file_id).to_node(db).syntax()),
}
}

impl ModuleDefinitionDiagnostic {
pub(super) fn add_to(
&self,
db: &impl DefDatabase,
owner: Module,
sink: &mut DiagnosticSink,
) {
match self {
ModuleDefinitionDiagnostic::DuplicateName { name, definition, first_definition } => {
let raw_items = db.raw_items(owner.file_id);
sink.push(DuplicateDefinition {
file: owner.file_id,
name: name.to_string(),
definition: syntax_ptr_from_def(db, owner, raw_items[*definition].kind),
first_definition: syntax_ptr_from_def(db, owner, raw_items[*first_definition].kind),
})
},
}
}
}
}
26 changes: 26 additions & 0 deletions crates/mun_hir/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,29 @@ impl Diagnostic for CannotApplyBinaryOp {
self
}
}

#[derive(Debug)]
pub struct DuplicateDefinition {
pub file: FileId,
pub name: String,
pub first_definition: SyntaxNodePtr,
pub definition: SyntaxNodePtr,
}

impl Diagnostic for DuplicateDefinition {
fn message(&self) -> String {
format!("the name `{}` is defined multiple times", self.name)
}

fn file(&self) -> FileId {
self.file
}

fn syntax_node_ptr(&self) -> SyntaxNodePtr {
self.definition
}

fn as_any(&self) -> &(dyn Any + Send + 'static) {
self
}
}
30 changes: 29 additions & 1 deletion crates/mun_syntax/src/ast/extensions.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use crate::ast::NameOwner;
use crate::{
ast::{self, AstNode},
T,
SyntaxKind, T,
};
use crate::{SmolStr, SyntaxNode};
use text_unit::TextRange;

impl ast::Name {
pub fn text(&self) -> &SmolStr {
Expand All @@ -16,6 +18,32 @@ impl ast::NameRef {
}
}

impl ast::FunctionDef {
pub fn signature_range(&self) -> TextRange {
let fn_kw = self
.syntax()
.children_with_tokens()
.find(|p| p.kind() == SyntaxKind::FN_KW)
.map(|kw| kw.text_range());
let name = self.name().map(|n| n.syntax.text_range());
let param_list = self.param_list().map(|p| p.syntax.text_range());
let ret_type = self.ret_type().map(|r| r.syntax.text_range());

let start = fn_kw
.map(|kw| kw.start())
.unwrap_or(self.syntax.text_range().start());

let end = ret_type
.map(|p| p.end())
.or(param_list.map(|name| name.end()))
.or(name.map(|name| name.end()))
.or(fn_kw.map(|kw| kw.end()))
.unwrap_or(self.syntax().text_range().end());

TextRange::from_to(start, end)
}
}

fn text_of_first_token(node: &SyntaxNode) -> &SmolStr {
match node.0.green().children().first() {
Some(rowan::GreenElement::Token(it)) => it.text(),
Expand Down

0 comments on commit 1a3a3a3

Please sign in to comment.