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
187 changes: 163 additions & 24 deletions ginko/src/dts/analysis.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
use itertools::Itertools;

use crate::dts::ast::{
ABSOLUTE_ROOT, AbsolutePath, AnyDirective, Cell, DtsFile, Include, LabelRelativePath, Node,
NodeItem, NodeName, NodePayload, Path, Primary, Property, PropertyPath, PropertyValue,
Reference, ReferencedNode, WithToken,
AbsolutePath, AnyDirective, Cell, DtsFile, Include, LabelRelativePath, Node, NodeItem,
NodeName, NodePayload, Path, Primary, Property, PropertyPath, PropertyValue, Reference,
ReferencedNode, WithToken, ABSOLUTE_ROOT,
};
use crate::dts::data::{HasSource, HasSpan, Span};
use crate::dts::error_codes::ErrorCode;
use crate::dts::import_guard::ImportGuard;
use crate::dts::loader::IncludeLoaderGuard;
use crate::dts::resolved::ResolvedTree;
use crate::dts::tokens::IncludeKind;
use crate::dts::visitor::ReferenceContext;
use crate::dts::{Diagnostic, FileType, Position, Project};
use std::collections::HashMap;
use crate::header::{self, Define, HeaderFile, IncludeStyle, TokenKind};
use std::collections::{BTreeMap, HashMap};
use std::path::{Path as StdPath, PathBuf};
use std::sync::Arc;

Expand Down Expand Up @@ -60,6 +62,7 @@ pub(crate) struct Analysis {
import_guard: ImportGuard<PathBuf>,
loader: IncludeLoaderGuard,
context: AnalysisContext,
headers: HashMap<PathBuf, HeaderFile>,
}

impl Analysis {
Expand All @@ -76,6 +79,7 @@ impl Analysis {
import_guard: ImportGuard::default(),
loader: loader.clone(),
context,
headers: HashMap::new(),
}
}

Expand All @@ -93,6 +97,7 @@ pub struct AnalysisContext {
flat_nodes: HashMap<AbsolutePath, Arc<Node>>,
includes: HashMap<String, PathBuf>,
resolved: ResolvedTree,
defines: BTreeMap<String, Define>,
}

pub(crate) struct FileAnalysisResult {
Expand All @@ -109,6 +114,10 @@ impl AnalysisContext {
self.labels.keys().cloned().collect()
}

pub fn get_define(&self, name: &str) -> Option<Define> {
self.defines.get(name).cloned()
}

pub fn get_resolved_tree(&self) -> &ResolvedTree {
&self.resolved
}
Expand Down Expand Up @@ -269,6 +278,10 @@ impl AnalysisContext {
}
}
}

pub fn resolve_value(&self, name: &str) -> Option<String> {
crate::header::resolve_value_str(&self.defines, name)
}
}

struct FileAnalysisContext<'a> {
Expand Down Expand Up @@ -353,7 +366,6 @@ impl Analysis {
self.analyze_referenced_node(&mut ctx, referenced_node.clone());
ctx.first_non_include = true
}
Primary::CStyleInclude(_) => {}
}
}
if !ctx.dts_header_seen && ctx.file_type == FileType::DtSource {
Expand All @@ -373,34 +385,115 @@ impl Analysis {
parent: &DtsFile,
include: &Include,
) {
let path = match self.loader.load(&parent.source, &include.file_name()) {
let path = match self
.loader
.load(&parent.source, &include.file_name(), include.kind())
{
Ok(path) => path,
Err(err) => {
ctx.add_diagnostic(Diagnostic::io_error(include.span(), include.source(), err));
return;
}
};
if let Err(err) = self
.import_guard
.add(path.clone(), &[parent.source.clone().to_path_buf()])
{
ctx.add_diagnostic(Diagnostic::cyclic_dependency_error(
include.span(),
include.source(),
err,
));
if include.follow_as_dts(&path) {
if let Err(err) = self
.import_guard
.add(path.clone(), &[parent.source.clone().to_path_buf()])
{
ctx.add_diagnostic(Diagnostic::cyclic_dependency_error(
include.span(),
include.source(),
err,
));
return;
}
let Some(proj_file) = ctx.project.get_file(&path) else {
return;
};
if proj_file.has_errors(&ctx.project.severities) {
ctx.add_diagnostic(Diagnostic::new(
include.span(),
include.source(),
ErrorCode::ErrorsInInclude,
"Included file contains errors",
));
}
}

if include.kind() == IncludeKind::Dts {
// dts-kind includes should not be followed for defines
return;
}
let Some(proj_file) = ctx.project.get_file(&path) else {

self.follow_header(
ctx,
path,
&mut vec![],
include.span(),
include.source().clone(),
);
}

fn follow_header(
&mut self,
ctx: &mut FileAnalysisContext<'_>,
path: PathBuf,
chain: &mut Vec<PathBuf>,
span: Span,
source: Arc<std::path::Path>,
) {
let Ok(canonical) = std::fs::canonicalize(&path) else {
// TODO: add diag
return;
};
if proj_file.has_errors(&ctx.project.severities) {
ctx.add_diagnostic(Diagnostic::new(
include.span(),
include.source(),
ErrorCode::ErrorsInInclude,
"Included file contains errors",
));

// cycle guard
if chain.contains(&canonical) {
return;
}

if let Some(_header) = self.headers.get(&canonical) {
// header already extracted, will get reused later
} else {
chain.push(canonical.clone());
let Ok(text) = std::fs::read_to_string(&canonical) else {
ctx.add_diagnostic(Diagnostic::new(
span,
source.clone(),
ErrorCode::IOError,
"failed to read header file",
));
return;
};

let header = header::parse_header(&text, canonical.clone().into());
for include in &header.includes {
let kind = match include.style {
IncludeStyle::Quoted => IncludeKind::CQuoted,
IncludeStyle::Chevron => IncludeKind::CAngled,
};
if let Ok(path) = self.loader.load(&canonical, &include.path, kind) {
self.follow_header(
ctx,
path,
chain,
include.target.span(),
include.target.source().clone(),
);
}
// TODO: handle load failure
}
chain.pop();
self.headers.insert(canonical.clone(), header);
}

for define in &self.headers[&canonical].defines {
if let TokenKind::Ident(name) = &define.name.kind {
self.context
.defines
.entry(name.clone())
.or_insert(define.clone());
}
}
}

Expand Down Expand Up @@ -682,6 +775,16 @@ impl Analysis {
Cell::Number(_, _) => {}
Cell::Reference(reference) => self.analyze_reference(ctx, reference),
Cell::Expression(_) => {}
Cell::Macro(token) => {
if self.context.get_define(token.item()).is_none() {
ctx.add_diagnostic(Diagnostic::new(
token.span(),
token.source(),
ErrorCode::UnknownMacro,
"Unknown macro",
));
}
}
}
}

Expand All @@ -694,7 +797,10 @@ impl Analysis {
}

fn analyze_incbin(&mut self, ctx: &mut FileAnalysisContext<'_>, include: &Include, span: Span) {
if let Err(err) = self.loader.load(&ctx.source, &include.file_name()) {
if let Err(err) = self
.loader
.load(&ctx.source, &include.file_name(), include.kind())
{
ctx.add_diagnostic(Diagnostic::io_error(span, include.source(), err));
}
}
Expand Down Expand Up @@ -1410,4 +1516,37 @@ labeled_referenced: &some_label {
]
)
}

#[test]
pub fn c_style_include_diagnostic() {
let code = Code::new(
r#"
/dts-v1/;

#include "x.h"
#include <x.h>

/ {
};
"#,
);
let (diagnostics, _) = code.get_analyzed_file();
assert_eq_unordered!(
diagnostics,
vec![
Diagnostic::new(
code.s1("#include \"x.h\"").span(),
code.source(),
ErrorCode::IOError,
"file not found: x.h"
),
Diagnostic::new(
code.s1("#include <x.h>").span(),
code.source(),
ErrorCode::IOError,
"file not found: x.h"
),
]
)
}
}
35 changes: 27 additions & 8 deletions ginko/src/dts/ast.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::dts::data::HasSource;
use crate::dts::tokens::Token;
use crate::dts::{HasSpan, Position, Span};
use crate::dts::tokens::{IncludeKind, IncludeName, Token};
use crate::dts::{FileType, HasSpan, Position, Span};
use itertools::Itertools;
use std::fmt::{Display, Formatter, LowerHex, Write};
use std::ops::Deref;
Expand Down Expand Up @@ -394,6 +394,7 @@ pub enum Cell {
Number(WithToken<u32>, NumberRepr),
Reference(WithToken<Reference>),
Expression(WithToken<String>),
Macro(WithToken<String>),
}

impl Cell {
Expand All @@ -413,6 +414,7 @@ impl Display for Cell {
Cell::Number(num, NumberRepr::Decimal) => write!(f, "{num}"),
Cell::Reference(reference) => write!(f, "{reference}"),
Cell::Expression(exp) => write!(f, "({exp})"),
Cell::Macro(name) => write!(f, "{name}"),
}
}
}
Expand Down Expand Up @@ -701,7 +703,7 @@ impl Display for DtsFile {
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct Include {
pub include_token: Token,
pub file_name: WithToken<String>,
pub file_name: WithToken<IncludeName>,
}

impl HasSpan for Include {
Expand All @@ -718,13 +720,33 @@ impl HasSource for Include {

impl Display for Include {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "/include/ \"{}\"", self.file_name)
write!(
f,
"{} {}",
self.include_token.kind.stringify(),
self.file_name
)
}
}

impl Include {
pub fn file_name(&self) -> String {
self.file_name.to_string()
self.file_name.path.to_string()
}

pub fn kind(&self) -> IncludeKind {
self.file_name.kind
}

pub fn follow_as_dts(&self, resolved: &std::path::Path) -> bool {
match self.kind() {
IncludeKind::Dts => true,
IncludeKind::CAngled => false,
IncludeKind::CQuoted => matches!(
FileType::from(resolved),
FileType::DtSource | FileType::DtSourceInclude | FileType::DtSourceOverlay
),
}
}
}

Expand Down Expand Up @@ -792,8 +814,6 @@ pub enum Primary {
Directive(AnyDirective),
Root(Arc<Node>),
ReferencedNode(Arc<ReferencedNode>),
// C-style includes should be put into a separate pass
CStyleInclude(String),
}

impl Primary {
Expand All @@ -811,7 +831,6 @@ impl Display for Primary {
Primary::Directive(directive) => write!(f, "{directive}"),
Primary::Root(node) => write!(f, "{node}"),
Primary::ReferencedNode(node) => write!(f, "{node}"),
Primary::CStyleInclude(include) => write!(f, "#include {include}"),
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions ginko/src/dts/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ impl Display for TokenKind {
TokenKind::Ref(_) => write!(f, "reference"),
TokenKind::Comment(_) => write!(f, "comment"),
TokenKind::Unknown(_) => write!(f, "unknown"),
TokenKind::PreprocessorDirective(preprocessor_directive) => {
write!(f, "{preprocessor_directive}")
}
TokenKind::IncludeName(_) => write!(f, "include name"),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions ginko/src/dts/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub enum ErrorCode {
NotAPropertyContext,
UnexpectedSize,
UnexpectedFormat,
UnknownMacro,
CustomError,
CustomWarning,
}
Expand Down Expand Up @@ -80,6 +81,7 @@ impl Default for SeverityMap {
| UnexpectedSize
| UnexpectedFormat
| DuplicateDirective
| UnknownMacro
| CustomWarning => Severity::Warning
};
SeverityMap { inner: map }
Expand Down
Loading